

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>


#pragma warning (disable : 4244)
#pragma warning (disable : 4996)

typedef unsigned char		byte;
typedef enum {false, true}	bool;

#define qfalse		false
#define qtrue		true
#define qboolean	bool

#include "aselib.h"
#include "mathlib.h"
#include "polylib.h"
#include "scriplib.h"
#include "surfaceflags.h"

#include "../lwobject/lwo2.h"


vec3_t vec3_origin = {0, 0, 0};


typedef enum {
	DFRM_NONE,
	DFRM_EXPAND,
	DFRM_MOVE,
	DFRM_SPRITE,
	DFRM_TUBE,
	DFRM_FLARE
} deform_t;

typedef struct {
	char	*name;
	int		surfType;
} surfTypeParm_t;

surfTypeParm_t	surfTypeParms[] = {
	{"grass",			SURFTYPE_GRASS},
	{"moss",			SURFTYPE_MOSS},
	{"rock",			SURFTYPE_ROCK},
	{"gravel",			SURFTYPE_GRAVEL},
	{"dirt",			SURFTYPE_DIRT},
	{"mud",				SURFTYPE_MUD},
	{"sand",			SURFTYPE_SAND},
	{"snow",			SURFTYPE_SNOW},
	{"ice",				SURFTYPE_ICE},
	{"liquid",			SURFTYPE_LIQUID},
	{"flesh",			SURFTYPE_FLESH},
	{"armor",			SURFTYPE_ARMOR},
	{"metal",			SURFTYPE_METAL},
	{"concrete",		SURFTYPE_CONCRETE},
	{"brick",			SURFTYPE_BRICK},
	{"plaster",			SURFTYPE_PLASTER},
	{"clay",			SURFTYPE_CLAY},
	{"marble",			SURFTYPE_MARBLE},
	{"ceramic",			SURFTYPE_CERAMIC},
	{"asphalt",			SURFTYPE_ASPHALT},
	{"wood",			SURFTYPE_WOOD},
	{"glass",			SURFTYPE_GLASS},
	{"bpGlass",			SURFTYPE_BPGLASS},
	{"plastic",			SURFTYPE_PLASTIC},
	{"rubber",			SURFTYPE_RUBBER},
	{"foam",			SURFTYPE_FOAM},
	{"cardboard",		SURFTYPE_CARDBOARD},
	{"carpet",			SURFTYPE_CARPET},
	{"cloth",			SURFTYPE_CLOTH},
	{"electronic",		SURFTYPE_ELECTRONIC},
	{"forceField",		SURFTYPE_FORCEFIELD}
};

typedef struct {
	char	*name;
	int		clearSolid, surfaceFlags, contents;
} infoParm_t;

infoParm_t	infoParms[] = {
	{"hint",			0,	SURFACE_HINT,		0},
	{"discrete",		0,	SURFACE_DISCRETE,	0},
	{"portalSky",		0,	SURFACE_PORTALSKY,	0},
	{"slick",			0,	SURFACE_SLICK,		0},
	{"bounce",			0,	SURFACE_BOUNCE,		0},
	{"ladder",			0,	SURFACE_LADDER,		0},
	{"noDamage",		0,	SURFACE_NODAMAGE,	0},
	{"noImpact",		0,	SURFACE_NOIMPACT,	0},
	{"noSteps",			0,	SURFACE_NOSTEPS,	0},
	{"noClip",			0,	SURFACE_NOCLIP,		0},
	{"noDraw",			0,	SURFACE_NODRAW,		0},
	{"noTJunc",			0,	SURFACE_NOTJUNC,	0},

	{"nonSolid",		1,	0,					0}, 
	{"solid",			0,	0,					CONTENT_SOLID},
	{"translucent",		0,	0,					CONTENT_TRANSLUCENT},
	{"water",			1,	0,					CONTENT_WATER},
	{"slime",			1,	0,					CONTENT_SLIME},
	{"lava",			1,	0,					CONTENT_LAVA},
	{"playerClip",		1,	0,					CONTENT_PLAYERCLIP},
	{"vehicleClip",		1,	0,					CONTENT_VEHICLECLIP},
	{"moveableClip",	1,	0,					CONTENT_MOVEABLECLIP},
	{"missileClip",		1,	0,					CONTENT_MISSILECLIP},
	{"fullClip",		1,	0,					CONTENT_PLAYERCLIP | CONTENT_VEHICLECLIP | CONTENT_MOVEABLECLIP | CONTENT_MISSILECLIP},
	{"detail",			0,	0,					CONTENT_DETAIL},
	{"structural",		0,	0,					CONTENT_STRUCTURAL},
	{"areaPortal",		1,	0,					CONTENT_AREAPORTAL},
	{"noDrop",			1,	0,					CONTENT_NODROP}
};


/*
===============
ParseShaderFile
===============
*/
static qboolean ParseShaderFile( const char *filename, const char *shader, int *surfaceFlags, int *contentFlags, int *deform, float *deformSize ) {
	int		i;
	int		numSurfTypeParms = sizeof(surfTypeParms) / sizeof(surfTypeParms[0]);
	int		numInfoParms = sizeof(infoParms) / sizeof(infoParms[0]);
	qboolean	found = qfalse;

//	qprintf( "shaderFile: %s\n", filename );
	LoadScriptFile( filename );
	while ( 1 ) {
		if ( !GetToken( qtrue ) ) {
			break;
		}

		// Skip tables
		if (!stricmp(token, "table")){
			qboolean	done = qfalse;
			int			depth = 0;

			// Parse the name
			if (!GetToken(qtrue))
				break;

			// Parse the definition
			do {
				if (!GetToken(qtrue)){
					done = qtrue;
					break;
				}

				if (!stricmp(token, "{"))
					depth++;
				else if	(!stricmp(token, "}"))
					depth--;
			} while (depth);

			if (done)
				break;

			continue;
		}

		if (!stricmp(token, shader))
			found = qtrue;

		MatchToken( "{" );
		while ( 1 ) {
			if ( !GetToken( qtrue ) ) {
				break;
			}
			if ( !strcmp( token, "}" ) ) {
				break;
			}

			// skip internal braced sections
			if ( !strcmp( token, "{" ) ) {
				while ( 1 ) {
					if ( !GetToken( qtrue ) ) {
						break;
					}
					if ( !strcmp( token, "}" ) ) {
						break;
					}
				}
				continue;
			}

			if (found){
				// deform <function> [parms...]
				// We catch these so we can try to hack the surface bounds to prevent
				// them from being culled out in game.
				// In most cases, this is far from perfect (although it usually helps
				// a bit, unless doing extreme deforms), but in certain specific cases
				// we can calculate the proper bounds and avoid visual glitches (ie: surfaces
				// suddenly disappearing as the camera moves when they should still be visible).
				if ( !stricmp( token, "deform" ) ) {
					GetToken( qfalse );

					if (!stricmp(token, "expand")){
						*deform = DFRM_EXPAND;

						// We rely on this being a constant to get it right
						GetToken( qfalse );
						*deformSize = atof(token);

						if (!*deformSize)
							*deformSize = 64.0f;		// It is likely a variable
					}
					else if (!stricmp(token, "move")){
						*deform = DFRM_MOVE;
						*deformSize = 64.0f;			// Bah...
					}
					else if (!stricmp(token, "sprite"))
						*deform = DFRM_SPRITE;
					else if (!stricmp(token, "tube"))
						*deform = DFRM_TUBE;
					else if (!stricmp(token, "flare")){
						*deform = DFRM_FLARE;

						// We rely on this being a constant to get it right
						GetToken( qfalse );
						*deformSize = atof(token);

						if (!*deformSize)
							*deformSize = 64.0f;		// It is likely a variable
					}

					// Ignore all other tokens on the line
					while (TokenAvailable()){
						*deformSize = 64.0f;			// It was an expression

						GetToken(qfalse);
					}

					continue;
				}

				// Surface types
				for ( i = 0 ; i < numSurfTypeParms ; i++ ) {
					if ( !stricmp( token, surfTypeParms[i].name ) ) {
						*surfaceFlags |= surfTypeParms[i].surfType;
						break;
					}
				}
				if ( i != numSurfTypeParms ) {
					continue;
				}

				// Surface parms
				for ( i = 0 ; i < numInfoParms ; i++ ) {
					if ( !stricmp( token, infoParms[i].name ) ) {
						*surfaceFlags |= infoParms[i].surfaceFlags;
						*contentFlags |= infoParms[i].contents;

						if ( infoParms[i].clearSolid ) {
							*contentFlags &= ~CONTENT_SOLID;
						}

						// translucent overrides opaque
						if (*contentFlags & CONTENT_TRANSLUCENT)
							*contentFlags &= ~CONTENT_OPAQUE;

						// slime overrides water
						if (*contentFlags & CONTENT_SLIME)
							*contentFlags &= ~CONTENT_WATER;

						// lava overrides water and slime
						if (*contentFlags & CONTENT_LAVA)
							*contentFlags &= ~(CONTENT_WATER | CONTENT_SLIME);

						// structural overrides detail
						if (*contentFlags & CONTENT_STRUCTURAL)
							*contentFlags &= ~CONTENT_DETAIL;

						break;
					}
				}
				if ( i != numInfoParms ) {
					continue;
				}
			}

			// ignore all other tokens on the line
			while ( TokenAvailable() ) {
				GetToken( qfalse );
			}
		}			

		if (found)
			return qtrue;
	}

	return qfalse;
}

/*
===============
LoadShaderInfo
===============
*/
#define	MAX_SHADER_FILES	512

char			*shaderFiles[MAX_SHADER_FILES];
int				numShaderFiles = 0;

void LoadShaderInfo( void ) {
	char			filename[1024];

	WIN32_FIND_DATA	findInfo;
	HANDLE			findHandle;
	BOOL			findRes = TRUE;

	sprintf( filename, "%smaterials/*", "base/" );

	findHandle = FindFirstFile(filename, &findInfo);
	if (findHandle == INVALID_HANDLE_VALUE){
		printf( "%5i shaderInfo\n", 0);
		return;
	}

	while (findRes == TRUE){
		// Check for invalid file name
		if (!stricmp(findInfo.cFileName, ".") || !stricmp(findInfo.cFileName, "..")){
			findRes = FindNextFile(findHandle, &findInfo);
			continue;
		}

		// Add it to the list
		if (numShaderFiles == MAX_SHADER_FILES)
			break;

		if (!(findInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
			if (!stricmp(&findInfo.cFileName[strlen(findInfo.cFileName)-4], ".mtr")){
				shaderFiles[numShaderFiles] = malloc(strlen(findInfo.cFileName) + 1);
				strcpy(shaderFiles[numShaderFiles], findInfo.cFileName);
				numShaderFiles++;
			}
		}

		findRes = FindNextFile(findHandle, &findInfo);
	}

	FindClose(findHandle);
}

void FlagsForShader (const char *shader, int *surfaceFlags, int *contentFlags, int *deform, float *deformSize){

	int		i;
	char	name[1024];

	*surfaceFlags = 0;
	*contentFlags = CONTENT_SOLID | CONTENT_OPAQUE;
	*deform = DFRM_NONE;
	*deformSize = 0.0f;

	if (!numShaderFiles)
		LoadShaderInfo();

	for (i = 0; i < numShaderFiles; i++){
		sprintf(name, "base/materials/%s", shaderFiles[i]);

		if (ParseShaderFile(name, shader, surfaceFlags, contentFlags, deform, deformSize))
			break;
	}
}


/*
 =======================================================================

 .MD3 file format

 =======================================================================
*/

#define MAX_QPATH			64

#define MD3_IDENT			(('3'<<24)+('P'<<16)+('D'<<8)+'I')
#define MD3_VERSION			15

#define MD3_MAX_TRIANGLES	8192			// Per surface
#define MD3_MAX_VERTS		4096			// Per surface
#define MD3_MAX_SHADERS		256				// Per surface
#define MD3_MAX_TAGS		16				// Per frame
#define MD3_MAX_FRAMES		1024			// Per model
#define MD3_MAX_SURFACES	32				// Per model

#define MD3_MAX_LODS		4

#define MD3_XYZ_SCALE		(1.0/64)		// Vertex scales

typedef struct {
	vec3_t			bounds[2];
	vec3_t			localOrigin;
	float			radius;
	char			name[16];
} md3Frame_t;

typedef struct {
	char			name[MAX_QPATH];
	vec3_t			origin;
	vec3_t			axis[3];
} md3Tag_t;

typedef struct {
	int				ident;

	char			name[MAX_QPATH];
	int				flags;

	int				numFrames;				// All surfaces in a model should have the same
	int				numShaders;				// All surfaces in a model should have the same
	int				numVerts;
	int				numTriangles;

	int				ofsTriangles;
	int				ofsShaders;				// Offset from start of md3Surface_t
	int				ofsSt;					// Texture coords are common for all frames
	int				ofsXyzNormals;			// numVerts * numFrames
	int				ofsEnd;					// Next surface follows
} md3Surface_t;

typedef struct {
	char			name[MAX_QPATH];
	int				shaderIndex;			// For in-game use
} md3Shader_t;

typedef struct {
	int				indexes[3];
} md3Triangle_t;

typedef struct {
	vec2_t			st;
} md3St_t;

typedef struct {
	short			xyz[3];
	short			normal;
} md3XyzNormal_t;

typedef struct {
	int				ident;
	int				version;

	char			name[MAX_QPATH];		// Model name
	int				flags;

	int				numFrames;
	int				numTags;			
	int				numSurfaces;
	int				numSkins;

	int				ofsFrames;				// Offset for first frame
	int				ofsTags;				// numTags * numFrames
	int				ofsSurfaces;			// First surface, others follow
	int				ofsEnd;					// End of file
} md3Header_t;

/*
 =======================================================================

 .MDL file format

 =======================================================================
*/

#define MDL_IDENT			((' '<<24)+('L'<<16)+('D'<<8)+'M')
#define MDL_VERSION			4

#define MDL_MAX_MESHES		32				// Per model
#define MDL_MAX_VERTICES	4096			// Per mesh
#define MDL_MAX_TRIANGLES	8192			// Per mesh

typedef struct {
	vec3_t			xyz;
	vec3_t			normal;
	vec3_t			tangents[2];
	vec2_t			st;
	byte			color[4];
} mdlVertex_t;

typedef struct {
	int				indices[3];
	int				neighbors[3];
} mdlTriangle_t;

typedef struct {
	vec3_t			normal;
	float			dist;
} mdlPlane_t;

typedef struct {
	char			material[256];

	vec3_t			mins;
	vec3_t			maxs;

	int				numVertices;
	int				ofsVertices;

	int				numTriangles;
	int				ofsTriangles;

	int				ofsFacePlanes;

	int				ofsEnd;
} mdlMesh_t;

typedef struct {
	int				ident;
	int				version;

	vec3_t			mins;
	vec3_t			maxs;

	int				numMeshes;
	int				ofsMeshes;
} mdlHeader_t;

/*
 =======================================================================

 .CM file format

 =======================================================================
*/

#define CM_IDENT			((' '<<24)+(' '<<16)+('M'<<8)+'C')
#define CM_VERSION			4

#define CM_MAX_MESHES		32				// Per model
#define CM_MAX_PLANES		16384			// Per mesh
#define CM_MAX_FACETS		2048			// Per mesh

typedef struct {
	vec3_t			normal;
	float			dist;
} cmPlane_t;

typedef struct {
	int				facetPlane;

	int				numBorders;				// 3 + 6 axial bevels + 3 * 4 edge bevels
	int				borderInward;			// Bit vector
	short			borderPlanes[21];
} cmFacet_t;

typedef struct {
	char			material[256];

	int				surfaceFlags;
	int				contentFlags;

	vec3_t			mins;
	vec3_t			maxs;

	int				numPlanes;
	int				ofsPlanes;

	int				numFacets;
	int				ofsFacets;

	int				ofsEnd;
} cmMesh_t;

typedef struct {
	int				ident;
	int				version;

	vec3_t			mins;
	vec3_t			maxs;

	int				numMeshes;
	int				ofsMeshes;
} cmHeader_t;


/*
 =======================================================================

 RENDER MODEL GENERATION

 =======================================================================
*/

typedef struct {
	mdlVertex_t *	vertices;
	mdlTriangle_t *	triangles;
	mdlPlane_t *	planes;

	mdlMesh_t		mesh;
} mesh_t;

static mesh_t	*modelMeshes;


/*
 =================
 DeformSurfaceBounds

 Ugly hack for culling
 =================
*/
void DeformSurfaceBounds (vec3_t mins, vec3_t maxs, deform_t deform, float size, vec3_t a, vec3_t b, vec3_t c){

	vec3_t			center, vecs[3];
	float			radius;
	float			length[3], shortest;

	if (deform == DFRM_NONE)
		return;

	switch (deform){
	case DFRM_EXPAND:
	case DFRM_MOVE:
	case DFRM_FLARE:
		// Just expand the bounds
		if (size <= 0.0f)
			break;

		mins[0] -= size;
		mins[1] -= size;
		mins[2] -= size;
		maxs[0] += size;
		maxs[1] += size;
		maxs[2] += size;

		break;
	case DFRM_SPRITE:
		// Find the approximate radius of the sprite
		radius = RadiusFromBounds(mins, maxs);

		center[0] = (mins[0] + maxs[0]) * 0.5f;
		center[1] = (mins[1] + maxs[1]) * 0.5f;
		center[2] = (mins[2] + maxs[2]) * 0.5f;

		mins[0] = center[0] - radius;
		mins[1] = center[1] - radius;
		mins[2] = center[2] - radius;
		maxs[0] = center[0] + radius;
		maxs[1] = center[1] + radius;
		maxs[2] = center[2] + radius;

		break;
	case DFRM_TUBE:
		// Find the shortest axis, then expand the bounds
		VectorSubtract(b, a, vecs[0]);
		VectorSubtract(c, a, vecs[1]);
		VectorSubtract(c, b, vecs[2]);

		length[0] = VectorLength(vecs[0]);
		length[1] = VectorLength(vecs[1]);
		length[2] = VectorLength(vecs[2]);

		shortest = length[0];
		if (length[1] < shortest)
			shortest = length[1];
		if (length[2] < shortest)
			shortest = length[2];

		shortest *= 0.5f;		// Half the length

		mins[0] -= shortest;
		mins[1] -= shortest;
		mins[2] -= shortest;
		maxs[0] += shortest;
		maxs[1] += shortest;
		maxs[2] += shortest;

		break;
	}
}

/*
 =================
 CalcTangentVectors
 =================
*/
static void CalcTangentVectors (int numTriangles, mdlTriangle_t *triangles, mdlPlane_t *planes, int numVertices, mdlVertex_t *vertices){

	mdlTriangle_t	*triangle;
	mdlPlane_t		*plane;
	mdlVertex_t		*vertex;
	mdlVertex_t		*v1, *v2, *v3;
	vec5_t			edge[2];
	vec3_t			normal, tangents[2], cross;
	float			d;
	int				i, j;

	// Clear
	for (i = 0, vertex = vertices; i < numVertices; i++, vertex++){
		VectorClear(vertex->normal);
		VectorClear(vertex->tangents[0]);
		VectorClear(vertex->tangents[1]);
	}

	// Calculate normal and tangent vectors
	for (i = 0, triangle = triangles, plane = planes; i < numTriangles; i++, triangle++, plane++){
		v1 = &vertices[triangle->indices[0]];
		v2 = &vertices[triangle->indices[1]];
		v3 = &vertices[triangle->indices[2]];

		// Find edges
		edge[0][0] = v2->xyz[0] - v1->xyz[0];
		edge[0][1] = v2->xyz[1] - v1->xyz[1];
		edge[0][2] = v2->xyz[2] - v1->xyz[2];
		edge[0][3] = v2->st[0] - v1->st[0];
		edge[0][4] = v2->st[1] - v1->st[1];

		edge[1][0] = v3->xyz[0] - v1->xyz[0];
		edge[1][1] = v3->xyz[1] - v1->xyz[1];
		edge[1][2] = v3->xyz[2] - v1->xyz[2];
		edge[1][3] = v3->st[0] - v1->st[0];
		edge[1][4] = v3->st[1] - v1->st[1];

		// Compute normal vector
		normal[0] = edge[1][1] * edge[0][2] - edge[1][2] * edge[0][1];
		normal[1] = edge[1][2] * edge[0][0] - edge[1][0] * edge[0][2];
		normal[2] = edge[1][0] * edge[0][1] - edge[1][1] * edge[0][0];

		VectorNormalize(normal);

		// Compute first tangent vector
		tangents[0][0] = edge[1][4] * edge[0][0] - edge[1][0] * edge[0][4];
		tangents[0][1] = edge[1][4] * edge[0][1] - edge[1][1] * edge[0][4];
		tangents[0][2] = edge[1][4] * edge[0][2] - edge[1][2] * edge[0][4];

		VectorNormalize(tangents[0]);

		// Compute second tangent vector
		tangents[1][0] = edge[1][0] * edge[0][3] - edge[1][3] * edge[0][0];
		tangents[1][1] = edge[1][1] * edge[0][3] - edge[1][3] * edge[0][1];
		tangents[1][2] = edge[1][2] * edge[0][3] - edge[1][3] * edge[0][2];

		d = DotProduct(tangents[1], normal);
		VectorMA(tangents[1], -d, normal, tangents[1]);
		VectorNormalize(tangents[1]);

		// Inverse tangent vectors if needed
		CrossProduct(tangents[1], tangents[0], cross);
		if (DotProduct(cross, normal) < 0){
			VectorInverse(tangents[0]);
			VectorInverse(tangents[1]);
		}

		// Add the vectors
		for (j = 0; j < 3; j++){
			VectorAdd(vertices[triangle->indices[j]].normal, normal, vertices[triangle->indices[j]].normal);
			VectorAdd(vertices[triangle->indices[j]].tangents[0], tangents[0], vertices[triangle->indices[j]].tangents[0]);
			VectorAdd(vertices[triangle->indices[j]].tangents[1], tangents[1], vertices[triangle->indices[j]].tangents[1]);
		}

		// Also set the triangle plane
		VectorCopy(normal, plane->normal);
		plane->dist = DotProduct(v1->xyz, normal);
	}

	// Renormalize
	for (i = 0, vertex = vertices; i < numVertices; i++, vertex++){
		VectorNormalize(vertex->normal);

		d = DotProduct(vertex->tangents[0], vertex->normal);
		VectorMA(vertex->tangents[0], -d, vertex->normal, vertex->tangents[0]);
		VectorNormalize(vertex->tangents[0]);

		d = DotProduct(vertex->tangents[1], vertex->normal);
		VectorMA(vertex->tangents[1], -d, vertex->normal, vertex->tangents[1]);
		VectorNormalize(vertex->tangents[1]);
	}
}

/*
 =================
 FindTriangleWithEdge
 =================
*/
static int FindTriangleWithEdge (int numTriangles, mdlTriangle_t *triangles, int start, int end, int ignore){

	mdlTriangle_t	*triangle;
	int				count, match;
	int				i;

	count = 0;
	match = numTriangles;

	for (i = 0, triangle = triangles; i < numTriangles; i++, triangle++){
		if ((triangle->indices[0] == start && triangle->indices[1] == end) || (triangle->indices[1] == start && triangle->indices[2] == end) || (triangle->indices[2] == start && triangle->indices[0] == end)){
			if (i != ignore)
				match = i;

			count++;
		}
		else if ((triangle->indices[1] == start && triangle->indices[0] == end) || (triangle->indices[2] == start && triangle->indices[1] == end) || (triangle->indices[0] == start && triangle->indices[2] == end))
			count++;
	}

	// Detect edges shared by three triangles and make them seams
	if (count > 2)
		match = numTriangles;

	return match;
}

/*
 =================
 BuildTriangleNeighbors
 =================
*/
static void BuildTriangleNeighbors (int numTriangles, mdlTriangle_t *triangles){

	mdlTriangle_t	*triangle;
	int				i;

	for (i = 0, triangle = triangles; i < numTriangles; i++, triangle++){
		triangle->neighbors[0] = FindTriangleWithEdge(numTriangles, triangles, triangle->indices[1], triangle->indices[0], i);
		triangle->neighbors[1] = FindTriangleWithEdge(numTriangles, triangles, triangle->indices[2], triangle->indices[1], i);
		triangle->neighbors[2] = FindTriangleWithEdge(numTriangles, triangles, triangle->indices[0], triangle->indices[2], i);
	}
}

/*
 =================
 ConvertMD3ToMDL
 =================
*/
static void ConvertMD3ToMDL (const char *srcName, const char *dstName){

	md3Header_t		*inHeader;
	md3Frame_t		*inFrame;
	md3Surface_t	*inSurface;
	md3Shader_t		*inShader;
	md3Triangle_t	*inTriangle;
	md3St_t			*inSt;
	md3XyzNormal_t	*inXyzNormal;
	mdlHeader_t		*outHeader;
	mesh_t			*outMesh;
	mdlTriangle_t	*outTriangle;
	mdlVertex_t		*outVertex;
	FILE			*f;
	byte			*data;
	int				surfaceFlags, contentFlags, deform;
	float			deformSize;
	int				i, j;

	// Load the file
	f = fopen(srcName, "rb");
	if (!f){
		printf("Couldn't find %s\n", srcName);
		return;
	}

	fseek(f, 0, SEEK_END);
	i = ftell(f);
	fseek(f, 0, SEEK_SET);

	data = malloc(i);
	fread(data, 1, i, f);
	fclose(f);

	inHeader = (void *)data;

	if (inHeader->ident != MD3_IDENT){
		printf("MD3 has wrong file id\n");
		return;
	}

	if (inHeader->version != MD3_VERSION){
		printf("MD3 has wrong version number (%i should be %i)\n", inHeader->version, MD3_VERSION);
		return;
	}

	if (inHeader->numFrames < 1){
		printf("MD3 has invalid number of frames (%i)\n", inHeader->numFrames);
		return;
	}

	if (inHeader->numSurfaces < 1 || inHeader->numSurfaces > MD3_MAX_SURFACES){
		printf("MD3 has invalid number of surfaces (%i)\n", inHeader->numSurfaces);
		return;
	}

	outHeader = malloc(sizeof(mdlHeader_t));

	// Copy the meshes
	inSurface = (void *)(data + inHeader->ofsSurfaces);

	outHeader->numMeshes = inHeader->numSurfaces;
	modelMeshes = outMesh = malloc(outHeader->numMeshes * sizeof(mesh_t));

	for (i = 0; i < outHeader->numMeshes; i++, outMesh++){
		if (inSurface->numShaders != 1){
			printf("MD3 has invalid number of shaders in a surface (%i)\n", inSurface->numShaders);
			return;
		}

		if (inSurface->numTriangles < 1 || inSurface->numTriangles > MD3_MAX_TRIANGLES){
			printf("MD3 has invalid number of triangles in a surface (%i)\n", inSurface->numTriangles);
			return;
		}

		if (inSurface->numVerts < 1 || inSurface->numVerts > MD3_MAX_VERTS){
			printf("MD3 has invalid number of vertices in a surface (%i)\n", inSurface->numVerts);
			return;
		}

		// Copy the material
		inShader = (void *)((byte *)inSurface + inSurface->ofsShaders);

		if (strchr(inShader->name, '.'))
			*strchr(inShader->name, '.') = 0;

		memset(outMesh->mesh.material, 0, sizeof(outMesh->mesh.material));
		strcpy(outMesh->mesh.material, inShader->name);

		// Copy triangle indices
		inTriangle = (void *)((byte *)inSurface + inSurface->ofsTriangles);

		outMesh->mesh.numTriangles = inSurface->numTriangles;
		outMesh->triangles = outTriangle = malloc(outMesh->mesh.numTriangles * sizeof(mdlTriangle_t));

		for (j = 0; j < outMesh->mesh.numTriangles; j++, inTriangle++, outTriangle++){
			outTriangle->indices[0] = inTriangle->indexes[0];
			outTriangle->indices[1] = inTriangle->indexes[1];
			outTriangle->indices[2] = inTriangle->indexes[2];
		}

		// Allocate space for planes
		outMesh->planes = malloc(outMesh->mesh.numTriangles * sizeof(mdlPlane_t));

		// Copy vertices
		inFrame = (void *)(data + inHeader->ofsFrames);
		inSt = (void *)((byte *)inSurface + inSurface->ofsSt);
		inXyzNormal = (void *)((byte *)inSurface + inSurface->ofsXyzNormals);

		outMesh->mesh.numVertices = inSurface->numVerts;
		outMesh->vertices = outVertex = malloc(outMesh->mesh.numVertices * sizeof(mdlVertex_t));

		for (j = 0; j < outMesh->mesh.numVertices; j++, inSt++, inXyzNormal++, outVertex++){
			outVertex->xyz[0] = ((float)inXyzNormal->xyz[0] * MD3_XYZ_SCALE) + inFrame->localOrigin[0];
			outVertex->xyz[1] = ((float)inXyzNormal->xyz[1] * MD3_XYZ_SCALE) + inFrame->localOrigin[1];
			outVertex->xyz[2] = ((float)inXyzNormal->xyz[2] * MD3_XYZ_SCALE) + inFrame->localOrigin[2];

			outVertex->st[0] = inSt->st[0];
			outVertex->st[1] = inSt->st[1];

			outVertex->color[0] = 255;
			outVertex->color[1] = 255;
			outVertex->color[2] = 255;
			outVertex->color[3] = 255;
		}

		// Compute tangents
		CalcTangentVectors(outMesh->mesh.numTriangles, outMesh->triangles, outMesh->planes, outMesh->mesh.numVertices, outMesh->vertices);

		// Compute triangle neighbors
		BuildTriangleNeighbors(outMesh->mesh.numTriangles, outMesh->triangles);

		// Skip to next surface
		inSurface = (void *)((byte *)inSurface + inSurface->ofsEnd);
	}

	// Compute bounds
	outHeader->mins[0] = outHeader->mins[1] = outHeader->mins[2] = 999999;
	outHeader->maxs[0] = outHeader->maxs[1] = outHeader->maxs[2] = -999999;

	for (i = 0, outMesh = modelMeshes; i < outHeader->numMeshes; i++, outMesh++){
		outMesh->mesh.mins[0] = outMesh->mesh.mins[1] = outMesh->mesh.mins[2] = 999999;
		outMesh->mesh.maxs[0] = outMesh->mesh.maxs[1] = outMesh->mesh.maxs[2] = -999999;

		for (j = 0; j < outMesh->mesh.numVertices; j++){
			if (outMesh->vertices[j].xyz[0] < outMesh->mesh.mins[0])
				outMesh->mesh.mins[0] = outMesh->vertices[j].xyz[0];
			if (outMesh->vertices[j].xyz[0] > outMesh->mesh.maxs[0])
				outMesh->mesh.maxs[0] = outMesh->vertices[j].xyz[0];

			if (outMesh->vertices[j].xyz[1] < outMesh->mesh.mins[1])
				outMesh->mesh.mins[1] = outMesh->vertices[j].xyz[1];
			if (outMesh->vertices[j].xyz[1] > outMesh->mesh.maxs[1])
				outMesh->mesh.maxs[1] = outMesh->vertices[j].xyz[1];

			if (outMesh->vertices[j].xyz[2] < outMesh->mesh.mins[2])
				outMesh->mesh.mins[2] = outMesh->vertices[j].xyz[2];
			if (outMesh->vertices[j].xyz[2] > outMesh->mesh.maxs[2])
				outMesh->mesh.maxs[2] = outMesh->vertices[j].xyz[2];
		}

		FlagsForShader(outMesh->mesh.material, &surfaceFlags, &contentFlags, &deform, &deformSize);

		DeformSurfaceBounds(outMesh->mesh.mins, outMesh->mesh.maxs, deform, deformSize, outMesh->vertices[0].xyz, outMesh->vertices[1].xyz, outMesh->vertices[2].xyz);

		if (outMesh->mesh.mins[0] < outHeader->mins[0])
			outHeader->mins[0] = outMesh->mesh.mins[0];
		if (outMesh->mesh.maxs[0] > outHeader->maxs[0])
			outHeader->maxs[0] = outMesh->mesh.maxs[0];

		if (outMesh->mesh.mins[1] < outHeader->mins[1])
			outHeader->mins[1] = outMesh->mesh.mins[1];
		if (outMesh->mesh.maxs[1] > outHeader->maxs[1])
			outHeader->maxs[1] = outMesh->mesh.maxs[1];

		if (outMesh->mesh.mins[2] < outHeader->mins[2])
			outHeader->mins[2] = outMesh->mesh.mins[2];
		if (outMesh->mesh.maxs[2] > outHeader->maxs[2])
			outHeader->maxs[2] = outMesh->mesh.maxs[2];

		// Also set offsets
		outMesh->mesh.ofsVertices = sizeof(mdlMesh_t);
		outMesh->mesh.ofsTriangles = outMesh->mesh.ofsVertices + (outMesh->mesh.numVertices * sizeof(mdlVertex_t));
		outMesh->mesh.ofsFacePlanes = outMesh->mesh.ofsTriangles + (outMesh->mesh.numTriangles * sizeof(mdlTriangle_t));
		outMesh->mesh.ofsEnd = outMesh->mesh.ofsFacePlanes + (outMesh->mesh.numTriangles * sizeof(mdlPlane_t));
	}

	outHeader->ident = MDL_IDENT;
	outHeader->version = MDL_VERSION;
	outHeader->ofsMeshes = sizeof(mdlHeader_t);

	// Write the file
	f = fopen(dstName, "wb");
	if (!f){
		printf("Couldn't write %s\n", dstName);
		return;
	}

	fwrite(outHeader, 1, sizeof(mdlHeader_t), f);

	for (i = 0, outMesh = modelMeshes; i < outHeader->numMeshes; i++, outMesh++){
		fwrite(&outMesh->mesh, 1, sizeof(mdlMesh_t), f);
		fwrite(outMesh->vertices, 1, outMesh->mesh.numVertices * sizeof(mdlVertex_t), f);
		fwrite(outMesh->triangles, 1, outMesh->mesh.numTriangles * sizeof(mdlTriangle_t), f);
		fwrite(outMesh->planes, 1, outMesh->mesh.numTriangles * sizeof(mdlPlane_t), f);

		free(outMesh->vertices);
		free(outMesh->triangles);
		free(outMesh->planes);
	}

	free(modelMeshes);
	free(outHeader);

	free(data);

	fclose(f);

	printf("Wrote %s\n", dstName);
}

/*
 =================
 ConvertASEToMDL
 =================
*/
static void ConvertASEToMDL (const char *srcName, const char *dstName){

	int					numSurfaces;
	const char			*name;
	polyset_t			*pset;
	int					numFrames;
	char				material[256];

	mdlHeader_t		*outHeader;
	mesh_t			*outMesh;
	mdlTriangle_t	*outTriangle;
	mdlVertex_t		*outVertex;
	FILE			*f;
	int				surfaceFlags, contentFlags, deform;
	float			deformSize;
	int				i, j, l;

	int				indexList[MDL_MAX_TRIANGLES * 3];
	int				numIndices;

	mdlVertex_t		vertexList[MDL_MAX_VERTICES], vertex;
	int				numVertices;

	// Load the file
	ASE_Load( srcName, qfalse, qfalse );

	outHeader = malloc(sizeof(mdlHeader_t));

	// Copy the meshes
	numSurfaces = ASE_GetNumSurfaces();

	outHeader->numMeshes = numSurfaces;
	modelMeshes = outMesh = malloc(outHeader->numMeshes * sizeof(mesh_t));

	for (i = 0; i < outHeader->numMeshes; i++, outMesh++){
		name = ASE_GetSurfaceName( i );

		pset = ASE_GetSurfaceAnimation( i, &numFrames, -1, -1, -1 );
		if ( !name || !pset ) {
			continue;
		}

		// Copy the material
		strcpy(material, pset->materialname);
		if (strchr(material, '.'))
			*strchr(material, '.') = 0;

		memset(outMesh->mesh.material, 0, sizeof(outMesh->mesh.material));
		strcpy(outMesh->mesh.material, material);

		// Copy vertices
		numIndices = 0;
		numVertices = 0;

		for ( j = 0 ; j < pset->numtriangles * 3 ; j++ ) {
			int		index;
			triangle_t	*tri;

			index = j % 3;
			tri = &pset->triangles[ j / 3 ];

			vertex.st[0] = tri->texcoords[index][0];
			vertex.st[1] = tri->texcoords[index][1];

			vertex.xyz[0] = tri->verts[index][0];
			vertex.xyz[1] = tri->verts[index][1];
			vertex.xyz[2] = tri->verts[index][2];

			vertex.color[0] = (byte)(255.0f * tri->colors[index][0]);
			vertex.color[1] = (byte)(255.0f * tri->colors[index][1]);
			vertex.color[2] = (byte)(255.0f * tri->colors[index][2]);
			vertex.color[3] = 255;

			// Add it to the vertex list if not added yet
			if (numIndices * 3 == MDL_MAX_TRIANGLES){
				printf("MDL_MAX_TRIANGLES hit\n");
				return;
			}

			for (l = 0; l < numVertices; l++){
				if (vertexList[l].xyz[0] != vertex.xyz[0] || vertexList[l].xyz[1] != vertex.xyz[1] || vertexList[l].xyz[2] != vertex.xyz[2])
					continue;
				if (vertexList[i].st[0] != vertex.st[0] || vertexList[i].st[1] != vertex.st[1])
					continue;

				break;
			}

			if (l == numVertices){
				if (numVertices == MDL_MAX_VERTICES){
					printf("MDL_MAX_VERTICES hit\n");
					return;
				}

				indexList[numIndices++] = numVertices;
				vertexList[numVertices++] = vertex;
			}
			else
				indexList[numIndices++] = l;
		}

		// Allocate space for triangles, vertices, and planes
		outMesh->mesh.numTriangles = numIndices / 3;
		outMesh->mesh.numVertices = numVertices;

		outMesh->triangles = outTriangle = malloc(outMesh->mesh.numTriangles * sizeof(mdlTriangle_t));
		outMesh->vertices = outVertex = malloc(outMesh->mesh.numVertices * sizeof(mdlVertex_t));
		outMesh->planes = malloc(outMesh->mesh.numTriangles * sizeof(mdlPlane_t));

		// Copy triangle indices
		for (j = 0; j < numIndices; j += 3, outTriangle++){
			outTriangle->indices[0] = indexList[j+0];
			outTriangle->indices[1] = indexList[j+1];
			outTriangle->indices[2] = indexList[j+2];
		}

		// Copy vertices
		for (j = 0; j < numVertices; j++, outVertex++){
			outVertex->xyz[0] = vertexList[j].xyz[0];
			outVertex->xyz[1] = vertexList[j].xyz[1];
			outVertex->xyz[2] = vertexList[j].xyz[2];

			outVertex->st[0] = vertexList[j].st[0];
			outVertex->st[1] = vertexList[j].st[1];

			outVertex->color[0] = vertexList[j].color[0];
			outVertex->color[1] = vertexList[j].color[1];
			outVertex->color[2] = vertexList[j].color[2];
			outVertex->color[3] = vertexList[j].color[3];
		}

		// Compute tangents
		CalcTangentVectors(outMesh->mesh.numTriangles, outMesh->triangles, outMesh->planes, outMesh->mesh.numVertices, outMesh->vertices);

		// Compute triangle neighbors
		BuildTriangleNeighbors(outMesh->mesh.numTriangles, outMesh->triangles);
	}

	// Compute bounds
	outHeader->mins[0] = outHeader->mins[1] = outHeader->mins[2] = 999999;
	outHeader->maxs[0] = outHeader->maxs[1] = outHeader->maxs[2] = -999999;

	for (i = 0, outMesh = modelMeshes; i < outHeader->numMeshes; i++, outMesh++){
		outMesh->mesh.mins[0] = outMesh->mesh.mins[1] = outMesh->mesh.mins[2] = 999999;
		outMesh->mesh.maxs[0] = outMesh->mesh.maxs[1] = outMesh->mesh.maxs[2] = -999999;

		for (j = 0; j < outMesh->mesh.numVertices; j++){
			if (outMesh->vertices[j].xyz[0] < outMesh->mesh.mins[0])
				outMesh->mesh.mins[0] = outMesh->vertices[j].xyz[0];
			if (outMesh->vertices[j].xyz[0] > outMesh->mesh.maxs[0])
				outMesh->mesh.maxs[0] = outMesh->vertices[j].xyz[0];

			if (outMesh->vertices[j].xyz[1] < outMesh->mesh.mins[1])
				outMesh->mesh.mins[1] = outMesh->vertices[j].xyz[1];
			if (outMesh->vertices[j].xyz[1] > outMesh->mesh.maxs[1])
				outMesh->mesh.maxs[1] = outMesh->vertices[j].xyz[1];

			if (outMesh->vertices[j].xyz[2] < outMesh->mesh.mins[2])
				outMesh->mesh.mins[2] = outMesh->vertices[j].xyz[2];
			if (outMesh->vertices[j].xyz[2] > outMesh->mesh.maxs[2])
				outMesh->mesh.maxs[2] = outMesh->vertices[j].xyz[2];
		}

		FlagsForShader(outMesh->mesh.material, &surfaceFlags, &contentFlags, &deform, &deformSize);

		DeformSurfaceBounds(outMesh->mesh.mins, outMesh->mesh.maxs, deform, deformSize, outMesh->vertices[0].xyz, outMesh->vertices[1].xyz, outMesh->vertices[2].xyz);

		if (outMesh->mesh.mins[0] < outHeader->mins[0])
			outHeader->mins[0] = outMesh->mesh.mins[0];
		if (outMesh->mesh.maxs[0] > outHeader->maxs[0])
			outHeader->maxs[0] = outMesh->mesh.maxs[0];

		if (outMesh->mesh.mins[1] < outHeader->mins[1])
			outHeader->mins[1] = outMesh->mesh.mins[1];
		if (outMesh->mesh.maxs[1] > outHeader->maxs[1])
			outHeader->maxs[1] = outMesh->mesh.maxs[1];

		if (outMesh->mesh.mins[2] < outHeader->mins[2])
			outHeader->mins[2] = outMesh->mesh.mins[2];
		if (outMesh->mesh.maxs[2] > outHeader->maxs[2])
			outHeader->maxs[2] = outMesh->mesh.maxs[2];

		// Also set offsets
		outMesh->mesh.ofsVertices = sizeof(mdlMesh_t);
		outMesh->mesh.ofsTriangles = outMesh->mesh.ofsVertices + (outMesh->mesh.numVertices * sizeof(mdlVertex_t));
		outMesh->mesh.ofsFacePlanes = outMesh->mesh.ofsTriangles + (outMesh->mesh.numTriangles * sizeof(mdlTriangle_t));
		outMesh->mesh.ofsEnd = outMesh->mesh.ofsFacePlanes + (outMesh->mesh.numTriangles * sizeof(mdlPlane_t));
	}

	outHeader->ident = MDL_IDENT;
	outHeader->version = MDL_VERSION;
	outHeader->ofsMeshes = sizeof(mdlHeader_t);

	// Write the file
	f = fopen(dstName, "wb");
	if (!f){
		printf("Couldn't write %s\n", dstName);
		return;
	}

	fwrite(outHeader, 1, sizeof(mdlHeader_t), f);

	for (i = 0, outMesh = modelMeshes; i < outHeader->numMeshes; i++, outMesh++){
		fwrite(&outMesh->mesh, 1, sizeof(mdlMesh_t), f);
		fwrite(outMesh->vertices, 1, outMesh->mesh.numVertices * sizeof(mdlVertex_t), f);
		fwrite(outMesh->triangles, 1, outMesh->mesh.numTriangles * sizeof(mdlTriangle_t), f);
		fwrite(outMesh->planes, 1, outMesh->mesh.numTriangles * sizeof(mdlPlane_t), f);

		free(outMesh->vertices);
		free(outMesh->triangles);
		free(outMesh->planes);
	}

	free(modelMeshes);
	free(outHeader);

	fclose(f);

	printf("Wrote %s\n", dstName);
}

/*
 =================
 ConvertLWOToMDL
 =================
*/
static void ConvertLWOToMDL (const char *srcName, const char *dstName){

	lwObject		*inObject;
	lwLayer			*inLayer;
	lwSurface		*inSurface;
	lwPolygon		*inPolygon;
	lwPolVert		*inPolVert;
	lwPoint			*inPoint;
	lwVMapPt		*inVMapPt;
	mdlHeader_t		*outHeader;
	mesh_t			*outMesh;
	mdlTriangle_t	*outTriangle;
	mdlVertex_t		*outVertex;
	FILE			*f;
	char			material[256];
	int				defaultSTAxis[2];
	float			defaultXYZ2STScale[2];
	float			min, max, size, st[2];
	unsigned		failId;
	int				failPos;
	int				surfaceFlags, contentFlags, deform;
	float			deformSize;
	int				i, j, k, l;

	int				indexList[MDL_MAX_TRIANGLES * 3];
	int				numIndices;

	mdlVertex_t		vertexList[MDL_MAX_VERTICES], vertex;
	int				numVertices;

	// Load the file
	inObject = lwGetObject(srcName, &failId, &failPos);
	if (!inObject){
		printf("Couldn't find %s\n", srcName);
		return;
	}

	if (inObject->nlayers != 1){
		printf("LWO has invalid number of layers (%i)\n", inObject->nlayers);
		return;
	}

	inLayer = &inObject->layer[0];

	outHeader = malloc(sizeof(mdlHeader_t));

	// Setup default ST map
	defaultSTAxis[0] = 0;
	defaultSTAxis[1] = 1;

	st[0] = 0.0f;
	st[1] = 0.0f;

	for (i = 0; i < 3; i++){
		min = inLayer->bbox[i+0];
		max = inLayer->bbox[i+3];

		size = max - min;

		if (size > st[0]){
			defaultSTAxis[1] = defaultSTAxis[0];
			defaultSTAxis[0] = i;

			st[1] = st[0];
			st[0] = size;
		}
		else if (size > st[1]){
			defaultSTAxis[1] = i;

			st[1] = size;
		}
	}

	defaultXYZ2STScale[0] = 4.0f / st[0];
	defaultXYZ2STScale[1] = 4.0f / st[1];

	// Copy the meshes
	inSurface = inObject->surf;

	outHeader->numMeshes = inObject->nsurfs;
	modelMeshes = outMesh = malloc(outHeader->numMeshes * sizeof(mesh_t));

	for (i = 0; i < outHeader->numMeshes; i++, outMesh++){
		// Copy the material
		strcpy(material, inSurface->name);
		if (strchr(material, '.'))
			*strchr(material, '.') = 0;

		memset(outMesh->mesh.material, 0, sizeof(outMesh->mesh.material));
		strcpy(outMesh->mesh.material, material);

		// Copy vertices
		numIndices = 0;
		numVertices = 0;

		for (j = 0, inPolygon = inLayer->polygon.pol; j < inLayer->polygon.count; j++, inPolygon++){
			if (inPolygon->surf != inSurface)
				continue;

			if (inPolygon->type != ID_FACE){
				printf("WARNING: polygon type != ID_FACE, discarded...\n");
				continue;
			}

			if (inPolygon->nverts != 3){
				printf("WARNING: polygon has %i vertices, discarded...\n", inPolygon->nverts);
				continue;
			}

			for (k = 0, inPolVert = inPolygon->v; k < 3; k++, inPolVert++){
				inPoint = &inLayer->point.pt[inPolVert->index];

				vertex.xyz[0] = inPoint->pos[0];
				vertex.xyz[1] = inPoint->pos[2];
				vertex.xyz[2] = inPoint->pos[1];

				vertex.st[0] = vertex.xyz[defaultSTAxis[0]] * defaultXYZ2STScale[0];
				vertex.st[1] = vertex.xyz[defaultSTAxis[1]] * defaultXYZ2STScale[1];

				vertex.color[0] = inSurface->color.rgb[0] * inSurface->diffuse.val * 255;
				vertex.color[1] = inSurface->color.rgb[1] * inSurface->diffuse.val * 255;
				vertex.color[2] = inSurface->color.rgb[2] * inSurface->diffuse.val * 255;
				vertex.color[3] = 255;

				for (l = 0, inVMapPt = inPoint->vm; l < inPoint->nvmaps; l++, inVMapPt++){
					if (inVMapPt->vmap->type == LWID_('T', 'X', 'U', 'V')){
						vertex.st[0] = inVMapPt->vmap->val[inVMapPt->index][0];
						vertex.st[1] = 1.0f - inVMapPt->vmap->val[inVMapPt->index][1];
					}

					if (inVMapPt->vmap->type == LWID_('R', 'G', 'B', 'A')){
						vertex.color[0] = inVMapPt->vmap->val[inVMapPt->index][0] * inSurface->color.rgb[0] * inSurface->diffuse.val * 255;
						vertex.color[1] = inVMapPt->vmap->val[inVMapPt->index][1] * inSurface->color.rgb[1] * inSurface->diffuse.val * 255;
						vertex.color[2] = inVMapPt->vmap->val[inVMapPt->index][2] * inSurface->color.rgb[2] * inSurface->diffuse.val * 255;
						vertex.color[3] = inVMapPt->vmap->val[inVMapPt->index][3] * 255;
					}
				}

				for (l = 0, inVMapPt = inPolVert->vm; l < inPolVert->nvmaps; l++, inVMapPt++){
					if (inVMapPt->vmap->type == LWID_('T', 'X', 'U', 'V')){
						vertex.st[0] = inVMapPt->vmap->val[inVMapPt->index][0];
						vertex.st[1] = 1.0f - inVMapPt->vmap->val[inVMapPt->index][1];
					}

					if (inVMapPt->vmap->type == LWID_('R', 'G', 'B', 'A')){
						vertex.color[0] = inVMapPt->vmap->val[inVMapPt->index][0] * inSurface->color.rgb[0] * inSurface->diffuse.val * 255;
						vertex.color[1] = inVMapPt->vmap->val[inVMapPt->index][1] * inSurface->color.rgb[1] * inSurface->diffuse.val * 255;
						vertex.color[2] = inVMapPt->vmap->val[inVMapPt->index][2] * inSurface->color.rgb[2] * inSurface->diffuse.val * 255;
						vertex.color[3] = inVMapPt->vmap->val[inVMapPt->index][3] * 255;
					}
				}

				// Add it to the vertex list if not added yet
				if (numIndices * 3 == MDL_MAX_TRIANGLES){
					printf("MDL_MAX_TRIANGLES hit\n");
					return;
				}

				for (l = 0; l < numVertices; l++){
					if (vertexList[l].xyz[0] != vertex.xyz[0] || vertexList[l].xyz[1] != vertex.xyz[1] || vertexList[l].xyz[2] != vertex.xyz[2])
						continue;
					if (vertexList[i].st[0] != vertex.st[0] || vertexList[i].st[1] != vertex.st[1])
						continue;

					break;
				}

				if (l == numVertices){
					if (numVertices == MDL_MAX_VERTICES){
						printf("MDL_MAX_VERTICES hit\n");
						return;
					}

					indexList[numIndices++] = numVertices;
					vertexList[numVertices++] = vertex;
				}
				else
					indexList[numIndices++] = l;
			}
		}

		// Allocate space for triangles, vertices, and planes
		outMesh->mesh.numTriangles = numIndices / 3;
		outMesh->mesh.numVertices = numVertices;

		outMesh->triangles = outTriangle = malloc(outMesh->mesh.numTriangles * sizeof(mdlTriangle_t));
		outMesh->vertices = outVertex = malloc(outMesh->mesh.numVertices * sizeof(mdlVertex_t));
		outMesh->planes = malloc(outMesh->mesh.numTriangles * sizeof(mdlPlane_t));

		// Copy triangle indices
		for (j = 0; j < numIndices; j += 3, outTriangle++){
			outTriangle->indices[0] = indexList[j+0];
			outTriangle->indices[1] = indexList[j+1];
			outTriangle->indices[2] = indexList[j+2];
		}

		// Copy vertices
		for (j = 0; j < numVertices; j++, outVertex++){
			outVertex->xyz[0] = vertexList[j].xyz[0];
			outVertex->xyz[1] = vertexList[j].xyz[1];
			outVertex->xyz[2] = vertexList[j].xyz[2];

			outVertex->st[0] = vertexList[j].st[0];
			outVertex->st[1] = vertexList[j].st[1];

			outVertex->color[0] = vertexList[j].color[0];
			outVertex->color[1] = vertexList[j].color[1];
			outVertex->color[2] = vertexList[j].color[2];
			outVertex->color[3] = vertexList[j].color[3];
		}

		// Compute tangents
		CalcTangentVectors(outMesh->mesh.numTriangles, outMesh->triangles, outMesh->planes, outMesh->mesh.numVertices, outMesh->vertices);

		// Compute triangle neighbors
		BuildTriangleNeighbors(outMesh->mesh.numTriangles, outMesh->triangles);

		// Skip to next surface
		inSurface = inSurface->next;
	}

	lwFreeObject(inObject);

	// Compute bounds
	outHeader->mins[0] = outHeader->mins[1] = outHeader->mins[2] = 999999;
	outHeader->maxs[0] = outHeader->maxs[1] = outHeader->maxs[2] = -999999;

	for (i = 0, outMesh = modelMeshes; i < outHeader->numMeshes; i++, outMesh++){
		outMesh->mesh.mins[0] = outMesh->mesh.mins[1] = outMesh->mesh.mins[2] = 999999;
		outMesh->mesh.maxs[0] = outMesh->mesh.maxs[1] = outMesh->mesh.maxs[2] = -999999;

		for (j = 0; j < outMesh->mesh.numVertices; j++){
			if (outMesh->vertices[j].xyz[0] < outMesh->mesh.mins[0])
				outMesh->mesh.mins[0] = outMesh->vertices[j].xyz[0];
			if (outMesh->vertices[j].xyz[0] > outMesh->mesh.maxs[0])
				outMesh->mesh.maxs[0] = outMesh->vertices[j].xyz[0];

			if (outMesh->vertices[j].xyz[1] < outMesh->mesh.mins[1])
				outMesh->mesh.mins[1] = outMesh->vertices[j].xyz[1];
			if (outMesh->vertices[j].xyz[1] > outMesh->mesh.maxs[1])
				outMesh->mesh.maxs[1] = outMesh->vertices[j].xyz[1];

			if (outMesh->vertices[j].xyz[2] < outMesh->mesh.mins[2])
				outMesh->mesh.mins[2] = outMesh->vertices[j].xyz[2];
			if (outMesh->vertices[j].xyz[2] > outMesh->mesh.maxs[2])
				outMesh->mesh.maxs[2] = outMesh->vertices[j].xyz[2];
		}

		FlagsForShader(outMesh->mesh.material, &surfaceFlags, &contentFlags, &deform, &deformSize);

		DeformSurfaceBounds(outMesh->mesh.mins, outMesh->mesh.maxs, deform, deformSize, outMesh->vertices[0].xyz, outMesh->vertices[1].xyz, outMesh->vertices[2].xyz);

		if (outMesh->mesh.mins[0] < outHeader->mins[0])
			outHeader->mins[0] = outMesh->mesh.mins[0];
		if (outMesh->mesh.maxs[0] > outHeader->maxs[0])
			outHeader->maxs[0] = outMesh->mesh.maxs[0];

		if (outMesh->mesh.mins[1] < outHeader->mins[1])
			outHeader->mins[1] = outMesh->mesh.mins[1];
		if (outMesh->mesh.maxs[1] > outHeader->maxs[1])
			outHeader->maxs[1] = outMesh->mesh.maxs[1];

		if (outMesh->mesh.mins[2] < outHeader->mins[2])
			outHeader->mins[2] = outMesh->mesh.mins[2];
		if (outMesh->mesh.maxs[2] > outHeader->maxs[2])
			outHeader->maxs[2] = outMesh->mesh.maxs[2];

		// Also set offsets
		outMesh->mesh.ofsVertices = sizeof(mdlMesh_t);
		outMesh->mesh.ofsTriangles = outMesh->mesh.ofsVertices + (outMesh->mesh.numVertices * sizeof(mdlVertex_t));
		outMesh->mesh.ofsFacePlanes = outMesh->mesh.ofsTriangles + (outMesh->mesh.numTriangles * sizeof(mdlTriangle_t));
		outMesh->mesh.ofsEnd = outMesh->mesh.ofsFacePlanes + (outMesh->mesh.numTriangles * sizeof(mdlPlane_t));
	}

	outHeader->ident = MDL_IDENT;
	outHeader->version = MDL_VERSION;
	outHeader->ofsMeshes = sizeof(mdlHeader_t);

	// Write the file
	f = fopen(dstName, "wb");
	if (!f){
		printf("Couldn't write %s\n", dstName);
		return;
	}

	fwrite(outHeader, 1, sizeof(mdlHeader_t), f);

	for (i = 0, outMesh = modelMeshes; i < outHeader->numMeshes; i++, outMesh++){
		fwrite(&outMesh->mesh, 1, sizeof(mdlMesh_t), f);
		fwrite(outMesh->vertices, 1, outMesh->mesh.numVertices * sizeof(mdlVertex_t), f);
		fwrite(outMesh->triangles, 1, outMesh->mesh.numTriangles * sizeof(mdlTriangle_t), f);
		fwrite(outMesh->planes, 1, outMesh->mesh.numTriangles * sizeof(mdlPlane_t), f);

		free(outMesh->vertices);
		free(outMesh->triangles);
		free(outMesh->planes);
	}

	free(modelMeshes);
	free(outHeader);

	fclose(f);

	printf("Wrote %s\n", dstName);
}


/*
 =======================================================================

 CLIP MODEL GENERATION

 =======================================================================
*/

#define CM_MAX_MAP_BOUNDS			65535


#define	MAX_TRIANGLE_FACETS	4096
#define	MAX_TRIANGLE_PLANES	32768

typedef struct {
	float	plane[4];
//	int		signBits;		// signx + (signy<<1) + (signz<<2), used as lookup during collision
} trianglePlane_t;

typedef struct {
	int			facetPlane;
	int			numBorders;		// 3 or 4 + 6 axial bevels + 4 or 3 * 4 edge bevels
	int			borderPlanes[4+6+16];
	int			borderInward[4+6+16];
} triangleFacet_t;

typedef struct triangleCollide_s {
	vec3_t	bounds[2];
	int		numPlanes;			// surface planes plus edge planes
	trianglePlane_t	*planes;
	int		numFacets;
	triangleFacet_t	*facets;
} triangleCollide_t;


typedef struct {
	int		numTris;
	vec3_t	points[MDL_MAX_TRIANGLES][3];
} cTrisoup_t;

#define	PLANE_TRI_EPSILON	0.1


/*
=================
CM_SignbitsForNormal
=================
*//*
static int CM_SignbitsForNormal( vec3_t normal ) {
	int	bits, j;

	bits = 0;
	for (j=0 ; j<3 ; j++) {
		if ( normal[j] < 0 ) {
			bits |= 1<<j;
		}
	}
	return bits;
}
*/
/*
=====================
CM_PlaneFromPoints

Returns false if the triangle is degenrate.
The normal will point out of the clock for clockwise ordered points
=====================
*/
static qboolean CM_PlaneFromPoints( vec4_t plane, vec3_t a, vec3_t b, vec3_t c ) {
	vec3_t	d1, d2;

	VectorSubtract( b, a, d1 );
	VectorSubtract( c, a, d2 );
	CrossProduct( d2, d1, plane );
	if ( VectorNormalize( plane ) == 0 ) {
		return qfalse;
	}

	plane[3] = DotProduct( a, plane );
	return qtrue;
}

/*
================================================================================

TRIANGLE COLLIDE GENERATION

================================================================================
*/

static	int				numPlanes;
static	trianglePlane_t	planes[MAX_TRIANGLE_PLANES];

static	int				numFacets;
static	triangleFacet_t	facets[MAX_TRIANGLE_PLANES]; //maybe MAX_TRIANGLE_FACETS ??

#define	NORMAL_EPSILON	0.0001
#define	DIST_EPSILON	0.02

static void Vector4Copy (vec4_t in, vec4_t out){

	out[0] = in[0];
	out[1] = in[1];
	out[2] = in[2];
	out[3] = in[3];
}

/*
==================
CM_PlaneEqual
==================
*/
static int CM_PlaneEqual(trianglePlane_t *p, float plane[4], int *flipped) {
	float invplane[4];

	if (
	   fabs(p->plane[0] - plane[0]) < NORMAL_EPSILON
	&& fabs(p->plane[1] - plane[1]) < NORMAL_EPSILON
	&& fabs(p->plane[2] - plane[2]) < NORMAL_EPSILON
	&& fabs(p->plane[3] - plane[3]) < DIST_EPSILON )
	{
		*flipped = qfalse;
		return qtrue;
	}

	invplane[0] = -plane[0];
	invplane[1] = -plane[1];
	invplane[2] = -plane[2];
	invplane[3] = -plane[3];

	if (
	   fabs(p->plane[0] - invplane[0]) < NORMAL_EPSILON
	&& fabs(p->plane[1] - invplane[1]) < NORMAL_EPSILON
	&& fabs(p->plane[2] - invplane[2]) < NORMAL_EPSILON
	&& fabs(p->plane[3] - invplane[3]) < DIST_EPSILON )
	{
		*flipped = qtrue;
		return qtrue;
	}

	return qfalse;
}

/*
==================
CM_SnapVector
==================
*/
static void CM_SnapVector(vec3_t normal) {
	int		i;

	for (i=0 ; i<3 ; i++)
	{
		if ( fabs(normal[i] - 1) < NORMAL_EPSILON )
		{
			VectorClear (normal);
			normal[i] = 1;
			break;
		}
		if ( fabs(normal[i] - -1) < NORMAL_EPSILON )
		{
			VectorClear (normal);
			normal[i] = -1;
			break;
		}
	}
}

/*
==================
CM_FindPlane2
==================
*/
static int CM_FindPlane2(float plane[4], int *flipped) {
	int i;

	// see if the points are close enough to an existing plane
	for ( i = 0 ; i < numPlanes ; i++ ) {
		if (CM_PlaneEqual(&planes[i], plane, flipped)) return i;
	}

	// add a new plane
	if ( numPlanes == MAX_TRIANGLE_PLANES ) {
		printf("MAX_TRIANGLE_PLANES" );
		exit(1);
	}

	Vector4Copy( plane, planes[numPlanes].plane );
//	planes[numPlanes].signBits = CM_SignbitsForNormal( plane );

	numPlanes++;

	*flipped = qfalse;

	return numPlanes-1;
}

/*
==================
CM_FindPlane
==================
*/
static int CM_FindPlane( float *p1, float *p2, float *p3 ) {
	float	plane[4];
	int		i;
	float	d;

	if ( !CM_PlaneFromPoints( plane, p1, p2, p3 ) ) {
		return -1;
	}

	// see if the points are close enough to an existing plane
	for ( i = 0 ; i < numPlanes ; i++ ) {
		if ( DotProduct( plane, planes[i].plane ) < 0 ) {
			continue;	// allow backwards planes?
		}

		d = DotProduct( p1, planes[i].plane ) - planes[i].plane[3];
		if ( d < -PLANE_TRI_EPSILON || d > PLANE_TRI_EPSILON ) {
			continue;
		}

		d = DotProduct( p2, planes[i].plane ) - planes[i].plane[3];
		if ( d < -PLANE_TRI_EPSILON || d > PLANE_TRI_EPSILON ) {
			continue;
		}

		d = DotProduct( p3, planes[i].plane ) - planes[i].plane[3];
		if ( d < -PLANE_TRI_EPSILON || d > PLANE_TRI_EPSILON ) {
			continue;
		}

		// found it
		return i;
	}

	// add a new plane
	if ( numPlanes == MAX_TRIANGLE_PLANES ) {
		printf("MAX_TRIANGLE_PLANES" );
		exit(1);
	}

	Vector4Copy( plane, planes[numPlanes].plane );
//	planes[numPlanes].signBits = CM_SignbitsForNormal( plane );

	numPlanes++;

	return numPlanes-1;
}

/*
==================
CM_PointOnPlaneSide
==================
*/
static int CM_PointOnPlaneSide( float *p, int planeNum ) {
	float	*plane;
	float	d;

	if ( planeNum == -1 ) {
		return SIDE_ON;
	}
	plane = planes[ planeNum ].plane;

	d = DotProduct( p, plane ) - plane[3];

	if ( d > PLANE_TRI_EPSILON ) {
		return SIDE_FRONT;
	}

	if ( d < -PLANE_TRI_EPSILON ) {
		return SIDE_BACK;
	}

	return SIDE_ON;
}

/*
==================
CM_TrianglePlane
==================
*/
static int	CM_TrianglePlane( int trianglePlanes[MAX_TRIANGLE_PLANES], int tri ) {
	int		p;

	p = trianglePlanes[tri];
	if ( p != -1 ) {
		return p;
	}

	// should never happen
	printf( "WARNING: CM_TrianglePlane unresolvable\n" );
	return -1;
}

/*
==================
CM_EdgePlaneNum
==================
*/
static int CM_EdgePlaneNum( cTrisoup_t *trisoup, int trianglePlanes[MAX_TRIANGLE_PLANES], int i, int k ) {
	float	*p1, *p2;
	vec3_t		up;
	int			p;

	switch ( k ) {
	case 0:	// top border
		p1 = trisoup->points[i][0];
		p2 = trisoup->points[i][1];
		p = CM_TrianglePlane( trianglePlanes, i);
		if (p == -1)
			return -1;
		VectorMA( p1, 4, planes[ p ].plane, up );
		return CM_FindPlane( p1, p2, up );

	case 2:	// bottom border
		p1 = trisoup->points[i][2];
		p2 = trisoup->points[i][0];
		p = CM_TrianglePlane( trianglePlanes, i);
		if (p == -1)
			return -1;
		VectorMA( p1, 4, planes[ p ].plane, up );
		return CM_FindPlane( p2, p1, up );

	case 1:	// right border
		p1 = trisoup->points[i][1];
		p2 = trisoup->points[i][2];
		p = CM_TrianglePlane( trianglePlanes, i);
		if (p == -1)
			return -1;
		VectorMA( p1, 4, planes[ p ].plane, up );
		return CM_FindPlane( p1, p2, up );
	}

	printf("CM_EdgePlaneNum: bad k" );
	exit(1);

	return -1;
}

/*
===================
CM_SetBorderInward
===================
*/
static void CM_SetBorderInward( triangleFacet_t *facet, cTrisoup_t *trisoup, int trianglePlanes[MAX_TRIANGLE_PLANES],
						  int i, int which ) {
	int		k, l;
	float	*points[4];
	int		numPoints;

	switch ( which ) {
//	case -1:
//		points[0] = grid->points[i][j];
//		points[1] = grid->points[i+1][j];
//		points[2] = grid->points[i+1][j+1];
//		points[3] = grid->points[i][j+1];
//		numPoints = 4;
//		break;
	case 0:
		points[0] = trisoup->points[i][0];
		points[1] = trisoup->points[i][1];
		points[2] = trisoup->points[i][2];
		numPoints = 3;
		break;
	case 1:
		points[0] = trisoup->points[i][2];
		points[1] = trisoup->points[i][1];
		points[2] = trisoup->points[i][0];
		numPoints = 3;
		break;
	default:
		printf("CM_SetBorderInward: bad parameter" );
		exit(1);

		numPoints = 0;
		break;
	}

	for ( k = 0 ; k < facet->numBorders ; k++ ) {
		int		front, back;

		front = 0;
		back = 0;

		for ( l = 0 ; l < numPoints ; l++ ) {
			int		side;

			side = CM_PointOnPlaneSide( points[l], facet->borderPlanes[k] );
			if ( side == SIDE_FRONT ) {
				front++;
			} if ( side == SIDE_BACK ) {
				back++;
			}
		}

		if ( front && !back ) {
			facet->borderInward[k] = qtrue;
		} else if ( back && !front ) {
			facet->borderInward[k] = qfalse;
		} else if ( !front && !back ) {
			// flat side border
			facet->borderPlanes[k] = -1;
		} else {
			// bisecting side border
			printf( "WARNING: CM_SetBorderInward: mixed plane sides\n" );
			facet->borderInward[k] = qfalse;
		}
	}
}

/*
==================
CM_ValidateFacet

If the facet isn't bounded by its borders, we screwed up.
==================
*/
static qboolean CM_ValidateFacet( triangleFacet_t *facet ) {
	float		plane[4];
	int			j;
	winding_t	*w;
	vec3_t		bounds[2];

	if ( facet->facetPlane == -1 ) {
		return qfalse;
	}

	Vector4Copy( planes[ facet->facetPlane ].plane, plane );
	w = BaseWindingForPlane( plane,  plane[3] );
	for ( j = 0 ; j < facet->numBorders && w ; j++ ) {
		if ( facet->borderPlanes[j] == -1 ) {
			return qfalse;
		}
		Vector4Copy( planes[ facet->borderPlanes[j] ].plane, plane );
		if ( !facet->borderInward[j] ) {
			VectorSubtract( vec3_origin, plane, plane );
			plane[3] = -plane[3];
		}
		ChopWindingInPlace( &w, plane, plane[3], 0.1f );
	}

	if ( !w ) {
		return qfalse;		// winding was completely chopped away
	}

	// see if the facet is unreasonably large
	WindingBounds( w, bounds[0], bounds[1] );
	FreeWinding( w );
	
	for ( j = 0 ; j < 3 ; j++ ) {
		if ( bounds[1][j] - bounds[0][j] > CM_MAX_MAP_BOUNDS ) {
			return qfalse;		// we must be missing a plane
		}
		if ( bounds[0][j] >= CM_MAX_MAP_BOUNDS ) {
			return qfalse;
		}
		if ( bounds[1][j] <= -CM_MAX_MAP_BOUNDS ) {
			return qfalse;
		}
	}
	return qtrue;		// winding is fine
}

/*
==================
CM_AddFacetBevels
==================
*/
static void CM_AddFacetBevels( triangleFacet_t *facet ) {

	int i, j, k, l;
	int axis, dir, order, flipped;
	float plane[4], d, newplane[4];
	winding_t *w, *w2;
	vec3_t mins, maxs, vec, vec2;

	Vector4Copy( planes[ facet->facetPlane ].plane, plane );

	w = BaseWindingForPlane( plane,  plane[3] );
	for ( j = 0 ; j < facet->numBorders && w ; j++ ) {
		if (facet->borderPlanes[j] == facet->facetPlane) continue;
		Vector4Copy( planes[ facet->borderPlanes[j] ].plane, plane );

		if ( !facet->borderInward[j] ) {
			VectorSubtract( vec3_origin, plane, plane );
			plane[3] = -plane[3];
		}

		ChopWindingInPlace( &w, plane, plane[3], 0.1f );
	}
	if ( !w ) {
		return;
	}

	WindingBounds(w, mins, maxs);

	// add the axial planes
	order = 0;
	for ( axis = 0 ; axis < 3 ; axis++ )
	{
		for ( dir = -1 ; dir <= 1 ; dir += 2, order++ )
		{
			VectorClear(plane);
			plane[axis] = dir;
			if (dir == 1) {
				plane[3] = maxs[axis];
			}
			else {
				plane[3] = -mins[axis];
			}
			//if it's the surface plane
			if (CM_PlaneEqual(&planes[facet->facetPlane], plane, &flipped)) {
				continue;
			}
			// see if the plane is allready present
			for ( i = 0 ; i < facet->numBorders ; i++ ) {
				if (CM_PlaneEqual(&planes[facet->borderPlanes[i]], plane, &flipped))
					break;
			}

			if ( i == facet->numBorders ) {
				if (facet->numBorders > 4 + 6 + 16) printf("ERROR: too many bevels\n");
				facet->borderPlanes[facet->numBorders] = CM_FindPlane2(plane, &flipped);
				facet->borderInward[facet->numBorders] = flipped;
				facet->numBorders++;
			}
		}
	}
	//
	// add the edge bevels
	//
	// test the non-axial plane edges
	for ( j = 0 ; j < w->numpoints ; j++ )
	{
		k = (j+1)%w->numpoints;
		VectorSubtract (w->p[j], w->p[k], vec);
		//if it's a degenerate edge
		if (VectorNormalize (vec) < 0.5)
			continue;
		CM_SnapVector(vec);
		for ( k = 0; k < 3 ; k++ )
			if ( vec[k] == -1 || vec[k] == 1 )
				break;	// axial
		if ( k < 3 )
			continue;	// only test non-axial edges

		// try the six possible slanted axials from this edge
		for ( axis = 0 ; axis < 3 ; axis++ )
		{
			for ( dir = -1 ; dir <= 1 ; dir += 2 )
			{
				// construct a plane
				VectorClear (vec2);
				vec2[axis] = dir;
				CrossProduct (vec, vec2, plane);
				if (VectorNormalize (plane) < 0.5)
					continue;
				plane[3] = DotProduct (w->p[j], plane);

				// if all the points of the facet winding are
				// behind this plane, it is a proper edge bevel
				for ( l = 0 ; l < w->numpoints ; l++ )
				{
					d = DotProduct (w->p[l], plane) - plane[3];
					if (d > 0.1)
						break;	// point in front
				}
				if ( l < w->numpoints )
					continue;

				//if it's the surface plane
				if (CM_PlaneEqual(&planes[facet->facetPlane], plane, &flipped)) {
					continue;
				}
				// see if the plane is allready present
				for ( i = 0 ; i < facet->numBorders ; i++ ) {
					if (CM_PlaneEqual(&planes[facet->borderPlanes[i]], plane, &flipped)) {
							break;
					}
				}

				if ( i == facet->numBorders ) {
					if (facet->numBorders > 4 + 6 + 16) printf("ERROR: too many bevels\n");
					facet->borderPlanes[facet->numBorders] = CM_FindPlane2(plane, &flipped);

					for ( k = 0 ; k < facet->numBorders ; k++ ) {
						if (facet->borderPlanes[facet->numBorders] ==
							facet->borderPlanes[k]) printf("WARNING: bevel plane already used\n");
					}

					facet->borderInward[facet->numBorders] = flipped;
					//
					w2 = CopyWinding(w);
					Vector4Copy(planes[facet->borderPlanes[facet->numBorders]].plane, newplane);
					if (!facet->borderInward[facet->numBorders])
					{
						newplane[0] = -newplane[0];
						newplane[1] = -newplane[1];
						newplane[2] = -newplane[2];
						newplane[3] = -newplane[3];
					} //end if
					ChopWindingInPlace( &w2, newplane, newplane[3], 0.1f );
					if (!w2) {
						printf("WARNING: CM_AddFacetBevels... invalid bevel\n");
						continue;
					}
					else {
						FreeWinding(w2);
					}
					//
					facet->numBorders++;
					//already got a bevel
//					break;
				}
			}
		}
	}
	FreeWinding( w );

	//add opposite plane
	facet->borderPlanes[facet->numBorders] = facet->facetPlane;
	facet->borderInward[facet->numBorders] = qtrue;
	facet->numBorders++;
}

typedef enum {
	EN_A,
	EN_B,
	EN_C
} edgeName_t;

/*
==================
CM_TriangleCollideFromTrisoup
==================
*/
static void CM_TriangleCollideFromTrisoup( cTrisoup_t *trisoup, triangleCollide_t *pf ) {
	int				i;
	float			*p1, *p2, *p3;
	int				trianglePlanes[MAX_TRIANGLE_PLANES];
	triangleFacet_t			*facet;
	int				borders[4];

	numPlanes = 0;
	numFacets = 0;

	// find the planes for each triangle of the trisoup
	for (i = 0; i < trisoup->numTris; i++){
		p1 = trisoup->points[i][0];
		p2 = trisoup->points[i][1];
		p3 = trisoup->points[i][2];
		trianglePlanes[i] = CM_FindPlane(p1, p2, p3);
	}

	// create the borders for each facet
	for ( i = 0 ; i < trisoup->numTris; i++ ) {
		borders[EN_A] = CM_EdgePlaneNum( trisoup, trianglePlanes, i, 0 );
		borders[EN_B] = CM_EdgePlaneNum( trisoup, trianglePlanes, i, 1 );
		borders[EN_C] = CM_EdgePlaneNum( trisoup, trianglePlanes, i, 2 );

		if ( numFacets == MAX_TRIANGLE_FACETS ) {
			printf("MAX_TRIANGLE_FACETS" );
			exit(1);
		}
		facet = &facets[numFacets];
		memset( facet, 0, sizeof( *facet ) );
/*
		if ( trianglePlanes[i] == trianglePlanes[i] ) {
			if ( trianglePlanes[i] == -1 ) {
				continue;		// degenrate
			}
			facet->facetPlane = trianglePlanes[i];
			facet->numBorders = 3;
			facet->borderPlanes[0] = borders[EN_A];
			facet->borderPlanes[1] = borders[EN_B];
			facet->borderPlanes[2] = borders[EN_C];
			CM_SetBorderInward( facet, trisoup, trianglePlanes, i, -1 );
			if ( CM_ValidateFacet( facet ) ) {
				CM_AddFacetBevels( facet );
				numFacets++;
			}
		} else {*/
			// two seperate triangles
			facet->facetPlane = trianglePlanes[i];
			facet->numBorders = 3;
			facet->borderPlanes[0] = borders[EN_A];
			facet->borderPlanes[1] = borders[EN_B];
			facet->borderPlanes[2] = borders[EN_C];
			CM_SetBorderInward( facet, trisoup, trianglePlanes, i, 0 );
			if ( CM_ValidateFacet( facet ) ) {
				CM_AddFacetBevels( facet );
				numFacets++;
			}
/*
			if ( numFacets == MAX_TRIANGLE_FACETS ) {
				Error("MAX_TRIANGLE_FACETS" );
			}
			facet = &facets[numFacets];
			memset( facet, 0, sizeof( *facet ) );

			facet->facetPlane = trianglePlanes[i];
			facet->numBorders = 3;
			facet->borderPlanes[0] = borders[EN_C];
			facet->borderPlanes[1] = borders[EN_B];
			facet->borderPlanes[2] = borders[EN_A];
			CM_SetBorderInward( facet, trisoup, trianglePlanes, i, 0 );
			if ( CM_ValidateFacet( facet ) ) {
				CM_AddFacetBevels( facet );
				numFacets++;
			}
			*/
//		}
	}

	// copy the results out
	pf->numPlanes = numPlanes;
	pf->numFacets = numFacets;
	pf->facets = malloc( numFacets * sizeof( *pf->facets ));
	memcpy( pf->facets, facets, numFacets * sizeof( *pf->facets ) );
	pf->planes = malloc( numPlanes * sizeof( *pf->planes ));
	memcpy( pf->planes, planes, numPlanes * sizeof( *pf->planes ) );
}

/*
===================
CM_GenerateTriangleCollide

Creates an internal structure that will be used to perform
collision detection with a trisoup mesh.
===================
*/
static struct triangleCollide_s 	*CM_GenerateTriangleCollide( int numPoints, vec3_t *points, int numTris, int *indices, int deform, float deformSize ) {
	triangleCollide_t	*pf;
	cTrisoup_t		trisoup;
	int				i;

	if ( numPoints <= 2 || !points ) {
		printf("CM_GenerateTriangleFacets: bad parameters: (%i, %p)",
			numPoints, points );
		exit(1);
	}

	// build a trisoup
	trisoup.numTris = numTris;
	for (i = 0; i < numTris; i++){
		VectorCopy(points[indices[i*3+0]], trisoup.points[i][0]);
		VectorCopy(points[indices[i*3+1]], trisoup.points[i][1]);
		VectorCopy(points[indices[i*3+2]], trisoup.points[i][2]);
	}

	// the surface defined by these points will be
	// collided against
	pf = malloc( sizeof( *pf ));
	ClearBounds( pf->bounds[0], pf->bounds[1] );
	for ( i = 0 ; i < numPoints; i++ ) {
		AddPointToBounds(points[i], pf->bounds[0], pf->bounds[1]);
	}

	DeformSurfaceBounds(pf->bounds[0], pf->bounds[1], deform, deformSize, trisoup.points[0][0], trisoup.points[0][1], trisoup.points[0][2]);

	// generate a bsp tree for the surface
	CM_TriangleCollideFromTrisoup( &trisoup, pf );

	// expand by one unit for epsilon purposes
//	pf->bounds[0][0] -= 1;
//	pf->bounds[0][1] -= 1;
//	pf->bounds[0][2] -= 1;

//	pf->bounds[1][0] += 1;
//	pf->bounds[1][1] += 1;
//	pf->bounds[1][2] += 1;

	return pf;
}







typedef struct {
	cmPlane_t *		planes;
	cmFacet_t *		facets;

	cmMesh_t		mesh;
} clipMesh_t;

static clipMesh_t	*clipMeshes;


/*
 =================
 ConvertMD3ToCM
 =================
*/
static void ConvertMD3ToCM (const char *srcName, const char *dstName){

	int							indices[MDL_MAX_TRIANGLES*3];
	vec3_t						points[MDL_MAX_VERTICES];
	struct triangleCollide_s	*pc;

	md3Header_t		*inHeader;
	md3Frame_t		*inFrame;
	md3Surface_t	*inSurface;
	md3Shader_t		*inShader;
	md3Triangle_t	*inTriangle;
	md3XyzNormal_t	*inXyzNormal;
	cmHeader_t		*outHeader;
	clipMesh_t		*outMesh;
	FILE			*f;
	byte			*data;
	int				deform;
	float			deformSize;
	int				i, j, k;

	// Load the file
	f = fopen(srcName, "rb");
	if (!f){
		printf("Couldn't find %s\n", srcName);
		return;
	}

	fseek(f, 0, SEEK_END);
	i = ftell(f);
	fseek(f, 0, SEEK_SET);

	data = malloc(i);
	fread(data, 1, i, f);
	fclose(f);

	inHeader = (void *)data;

	if (inHeader->ident != MD3_IDENT){
		printf("MD3 has wrong file id\n");
		return;
	}

	if (inHeader->version != MD3_VERSION){
		printf("MD3 has wrong version number (%i should be %i)\n", inHeader->version, MD3_VERSION);
		return;
	}

	if (inHeader->numFrames < 1){
		printf("MD3 has invalid number of frames (%i)\n", inHeader->numFrames);
		return;
	}

	if (inHeader->numSurfaces < 1 || inHeader->numSurfaces > MD3_MAX_SURFACES){
		printf("MD3 has invalid number of surfaces (%i)\n", inHeader->numSurfaces);
		return;
	}

	outHeader = malloc(sizeof(cmHeader_t));

	// Copy the meshes
	inSurface = (void *)(data + inHeader->ofsSurfaces);

	outHeader->numMeshes = inHeader->numSurfaces;
	clipMeshes = outMesh = malloc(inHeader->numSurfaces * sizeof(clipMesh_t));

	for (i = 0; i < outHeader->numMeshes; i++, outMesh++){
		if (inSurface->numShaders != 1){
			printf("MD3 has invalid number of shaders in a surface (%i)\n", inSurface->numShaders);
			return;
		}

		if (inSurface->numTriangles < 1 || inSurface->numTriangles > MD3_MAX_TRIANGLES){
			printf("MD3 has invalid number of triangles in a surface (%i)\n", inSurface->numTriangles);
			return;
		}

		if (inSurface->numVerts < 1 || inSurface->numVerts > MD3_MAX_VERTS){
			printf("MD3 has invalid number of vertices in a surface (%i)\n", inSurface->numVerts);
			return;
		}

		// Copy the material
		inShader = (void *)((byte *)inSurface + inSurface->ofsShaders);

		if (strchr(inShader->name, '.'))
			*strchr(inShader->name, '.') = 0;

		memset(outMesh->mesh.material, 0, sizeof(outMesh->mesh.material));
		strcpy(outMesh->mesh.material, inShader->name);

		FlagsForShader(outMesh->mesh.material, &outMesh->mesh.surfaceFlags, &outMesh->mesh.contentFlags, &deform, &deformSize);

		// Copy triangle indices
		inTriangle = (void *)((byte *)inSurface + inSurface->ofsTriangles);

		for (j = 0; j < inSurface->numTriangles; j++, inTriangle++){
			indices[j*3+0] = inTriangle->indexes[0];
			indices[j*3+1] = inTriangle->indexes[1];
			indices[j*3+2] = inTriangle->indexes[2];
		}

		// Copy vertices
		inFrame = (void *)(data + inHeader->ofsFrames);
		inXyzNormal = (void *)((byte *)inSurface + inSurface->ofsXyzNormals);

		for (j = 0; j < inSurface->numVerts; j++, inXyzNormal++){
			points[j][0] = ((float)inXyzNormal->xyz[0] * MD3_XYZ_SCALE) + inFrame->localOrigin[0];
			points[j][1] = ((float)inXyzNormal->xyz[1] * MD3_XYZ_SCALE) + inFrame->localOrigin[1];
			points[j][2] = ((float)inXyzNormal->xyz[2] * MD3_XYZ_SCALE) + inFrame->localOrigin[2];
		}

		// Create the internal facet structure
		pc = CM_GenerateTriangleCollide(inSurface->numVerts, points, inSurface->numTriangles, indices, deform, deformSize);

		outMesh->mesh.numPlanes = pc->numPlanes;
		if (outMesh->mesh.numPlanes > CM_MAX_PLANES){
			printf("CM_MAX_PLANES");
			exit(1);
		}

		outMesh->planes = malloc(pc->numPlanes * sizeof(cmPlane_t));

		for (j = 0; j < pc->numPlanes; j++){
			outMesh->planes[j].normal[0] = pc->planes[j].plane[0];
			outMesh->planes[j].normal[1] = pc->planes[j].plane[1];
			outMesh->planes[j].normal[2] = pc->planes[j].plane[2];
			outMesh->planes[j].dist = pc->planes[j].plane[3];
		}

		outMesh->mesh.numFacets = pc->numFacets;
		if (outMesh->mesh.numFacets > CM_MAX_FACETS){
			printf("CM_MAX_FACETS");
			exit(1);
		}

		outMesh->facets = malloc(pc->numFacets * sizeof(cmFacet_t));

		for (j = 0; j < pc->numFacets; j++){
			if (pc->facets[j].numBorders > 21){
				printf("numBorders > 21");
				exit(1);
			}

			outMesh->facets[j].facetPlane = pc->facets[j].facetPlane;
			outMesh->facets[j].numBorders = pc->facets[j].numBorders;
			outMesh->facets[j].borderInward = 0;

			for (k = 0; k < 21; k++){
				outMesh->facets[j].borderPlanes[k] = pc->facets[j].borderPlanes[k];

				// To save space, we pack these into a single integer
				if (pc->facets[j].borderInward[k])
					outMesh->facets[j].borderInward |= (1<<k);
			}
		}

		VectorCopy(pc->bounds[0], outMesh->mesh.mins);
		VectorCopy(pc->bounds[1], outMesh->mesh.maxs);

		// Skip to next surface
		inSurface = (void *)((byte *)inSurface + inSurface->ofsEnd);
	}

	// Compute bounds
	outHeader->mins[0] = outHeader->mins[1] = outHeader->mins[2] = 999999;
	outHeader->maxs[0] = outHeader->maxs[1] = outHeader->maxs[2] = -999999;

	for (i = 0, outMesh = clipMeshes; i < outHeader->numMeshes; i++, outMesh++){
		AddPointToBounds(outMesh->mesh.mins, outHeader->mins, outHeader->maxs);
		AddPointToBounds(outMesh->mesh.maxs, outHeader->mins, outHeader->maxs);

		// Also set offsets
		outMesh->mesh.ofsPlanes = sizeof(cmMesh_t);
		outMesh->mesh.ofsFacets = outMesh->mesh.ofsPlanes + (outMesh->mesh.numPlanes * sizeof(cmPlane_t));
		outMesh->mesh.ofsEnd = outMesh->mesh.ofsFacets + (outMesh->mesh.numFacets * sizeof(cmFacet_t));
	}

	outHeader->ident = CM_IDENT;
	outHeader->version = CM_VERSION;
	outHeader->ofsMeshes = sizeof(cmHeader_t);

	// Write the file
	f = fopen(dstName, "wb");
	if (!f){
		printf("Couldn't write %s\n", dstName);
		return;
	}

	fwrite(outHeader, 1, sizeof(cmHeader_t), f);

	for (i = 0, outMesh = clipMeshes; i < outHeader->numMeshes; i++, outMesh++){
		fwrite(&outMesh->mesh, 1, sizeof(cmMesh_t), f);
		fwrite(outMesh->planes, 1, outMesh->mesh.numPlanes * sizeof(cmPlane_t), f);
		fwrite(outMesh->facets, 1, outMesh->mesh.numFacets * sizeof(cmFacet_t), f);

		free(outMesh->planes);
		free(outMesh->facets);
	}

	free(clipMeshes);
	free(outHeader);

	free(data);

	fclose(f);

	printf("Wrote %s\n", dstName);
}

/*
 =================
 ConvertASEToCM
 =================
*/
static void ConvertASEToCM (const char *srcName, const char *dstName){

	int							indices[MDL_MAX_TRIANGLES*3];
	vec3_t						points[MDL_MAX_VERTICES];
	struct triangleCollide_s	*pc;

	int					numSurfaces;
	const char			*name;
	polyset_t			*pset;
	int					numFrames;
	char				material[256];

	cmHeader_t		*outHeader;
	clipMesh_t		*outMesh;
	FILE			*f;
	int				deform;
	float			deformSize;
	int				i, j, k, l;

	int				indexList[MDL_MAX_TRIANGLES];
	int				numIndices;

	mdlVertex_t		vertexList[MDL_MAX_VERTICES], vertex;
	int				numVertices;

	// Load the file
	ASE_Load( srcName, qfalse, qfalse );

	outHeader = malloc(sizeof(cmHeader_t));

	// Copy the meshes
	numSurfaces = ASE_GetNumSurfaces();

	outHeader->numMeshes = numSurfaces;
	clipMeshes = outMesh = malloc(numSurfaces * sizeof(clipMesh_t));

	for (i = 0; i < outHeader->numMeshes; i++, outMesh++){
		name = ASE_GetSurfaceName( i );

		pset = ASE_GetSurfaceAnimation( i, &numFrames, -1, -1, -1 );
		if ( !name || !pset ) {
			continue;
		}

		// Copy the material
		strcpy(material, pset->materialname);
		if (strchr(material, '.'))
			*strchr(material, '.') = 0;

		memset(outMesh->mesh.material, 0, sizeof(outMesh->mesh.material));
		strcpy(outMesh->mesh.material, material);

		FlagsForShader(outMesh->mesh.material, &outMesh->mesh.surfaceFlags, &outMesh->mesh.contentFlags, &deform, &deformSize);

		// Copy vertices
		numIndices = 0;
		numVertices = 0;

		for ( j = 0 ; j < pset->numtriangles * 3 ; j++ ) {
			int		index;
			triangle_t	*tri;

			index = j % 3;
			tri = &pset->triangles[ j / 3 ];

			vertex.st[0] = tri->texcoords[index][0];
			vertex.st[1] = tri->texcoords[index][1];

			vertex.xyz[0] = tri->verts[index][0];
			vertex.xyz[1] = tri->verts[index][1];
			vertex.xyz[2] = tri->verts[index][2];

			vertex.color[0] = (byte)(255.0f * tri->colors[index][0]);
			vertex.color[1] = (byte)(255.0f * tri->colors[index][1]);
			vertex.color[2] = (byte)(255.0f * tri->colors[index][2]);
			vertex.color[3] = 255;

			// Add it to the vertex list if not added yet
			if (numIndices * 3 == MDL_MAX_TRIANGLES){
				printf("MDL_MAX_TRIANGLES hit\n");
				return;
			}

			for (l = 0; l < numVertices; l++){
				if (vertexList[l].xyz[0] != vertex.xyz[0] || vertexList[l].xyz[1] != vertex.xyz[1] || vertexList[l].xyz[2] != vertex.xyz[2])
					continue;
				if (vertexList[i].st[0] != vertex.st[0] || vertexList[i].st[1] != vertex.st[1])
					continue;

				break;
			}

			if (l == numVertices){
				if (numVertices == MDL_MAX_VERTICES){
					printf("MDL_MAX_VERTICES hit\n");
					return;
				}

				indexList[numIndices++] = numVertices;
				vertexList[numVertices++] = vertex;
			}
			else
				indexList[numIndices++] = l;
		}

		// Copy triangle indices
		for (j = 0; j < numIndices; j += 3){
			indices[j*3+0] = indexList[j+0];
			indices[j*3+1] = indexList[j+1];
			indices[j*3+2] = indexList[j+2];
		}

		// Copy vertices
		for (j = 0; j < numVertices; j++){
			points[j][0] = vertexList[j].xyz[0];
			points[j][1] = vertexList[j].xyz[1];
			points[j][2] = vertexList[j].xyz[2];
		}

		// Create the internal facet structure
		pc = CM_GenerateTriangleCollide(numVertices, points, numIndices / 3, indices, deform, deformSize);

		outMesh->mesh.numPlanes = pc->numPlanes;
		if (outMesh->mesh.numPlanes > CM_MAX_PLANES){
			printf("CM_MAX_PLANES");
			exit(1);
		}

		outMesh->planes = malloc(pc->numPlanes * sizeof(cmPlane_t));

		for (j = 0; j < pc->numPlanes; j++){
			outMesh->planes[j].normal[0] = pc->planes[j].plane[0];
			outMesh->planes[j].normal[1] = pc->planes[j].plane[1];
			outMesh->planes[j].normal[2] = pc->planes[j].plane[2];
			outMesh->planes[j].dist = pc->planes[j].plane[3];
		}

		outMesh->mesh.numFacets = pc->numFacets;
		if (outMesh->mesh.numFacets > CM_MAX_FACETS){
			printf("CM_MAX_FACETS");
			exit(1);
		}

		outMesh->facets = malloc(pc->numFacets * sizeof(cmFacet_t));

		for (j = 0; j < pc->numFacets; j++){
			if (pc->facets[j].numBorders > 21){
				printf("numBorders > 21");
				exit(1);
			}

			outMesh->facets[j].facetPlane = pc->facets[j].facetPlane;
			outMesh->facets[j].numBorders = pc->facets[j].numBorders;
			outMesh->facets[j].borderInward = 0;

			for (k = 0; k < 21; k++){
				outMesh->facets[j].borderPlanes[k] = pc->facets[j].borderPlanes[k];

				// To save space, we pack these into a single integer
				if (pc->facets[j].borderInward[k])
					outMesh->facets[j].borderInward |= (1<<k);
			}
		}

		VectorCopy(pc->bounds[0], outMesh->mesh.mins);
		VectorCopy(pc->bounds[1], outMesh->mesh.maxs);
	}

	// Compute bounds
	outHeader->mins[0] = outHeader->mins[1] = outHeader->mins[2] = 999999;
	outHeader->maxs[0] = outHeader->maxs[1] = outHeader->maxs[2] = -999999;

	for (i = 0, outMesh = clipMeshes; i < outHeader->numMeshes; i++, outMesh++){
		AddPointToBounds(outMesh->mesh.mins, outHeader->mins, outHeader->maxs);
		AddPointToBounds(outMesh->mesh.maxs, outHeader->mins, outHeader->maxs);

		// Also set offsets
		outMesh->mesh.ofsPlanes = sizeof(cmMesh_t);
		outMesh->mesh.ofsFacets = outMesh->mesh.ofsPlanes + (outMesh->mesh.numPlanes * sizeof(cmPlane_t));
		outMesh->mesh.ofsEnd = outMesh->mesh.ofsFacets + (outMesh->mesh.numFacets * sizeof(cmFacet_t));
	}

	outHeader->ident = CM_IDENT;
	outHeader->version = CM_VERSION;
	outHeader->ofsMeshes = sizeof(cmHeader_t);

	// Write the file
	f = fopen(dstName, "wb");
	if (!f){
		printf("Couldn't write %s\n", dstName);
		return;
	}

	fwrite(outHeader, 1, sizeof(cmHeader_t), f);

	for (i = 0, outMesh = clipMeshes; i < outHeader->numMeshes; i++, outMesh++){
		fwrite(&outMesh->mesh, 1, sizeof(cmMesh_t), f);
		fwrite(outMesh->planes, 1, outMesh->mesh.numPlanes * sizeof(cmPlane_t), f);
		fwrite(outMesh->facets, 1, outMesh->mesh.numFacets * sizeof(cmFacet_t), f);

		free(outMesh->planes);
		free(outMesh->facets);
	}

	free(clipMeshes);
	free(outHeader);

	fclose(f);

	printf("Wrote %s\n", dstName);
}

/*
 =================
 ConvertLWOToCM
 =================
*/
static void ConvertLWOToCM (const char *srcName, const char *dstName){

	int							indices[MDL_MAX_TRIANGLES*3];
	vec3_t						points[MDL_MAX_VERTICES];
	struct triangleCollide_s	*pc;

	lwObject		*inObject;
	lwLayer			*inLayer;
	lwSurface		*inSurface;
	lwPolygon		*inPolygon;
	lwPolVert		*inPolVert;
	lwPoint			*inPoint;
	lwVMapPt		*inVMapPt;
	cmHeader_t		*outHeader;
	clipMesh_t		*outMesh;
	FILE			*f;
	char			material[256];
	int				defaultSTAxis[2];
	float			defaultXYZ2STScale[2];
	float			min, max, size, st[2];
	unsigned		failId;
	int				failPos;
	int				deform;
	float			deformSize;
	int				i, j, k, l;

	int				indexList[MDL_MAX_TRIANGLES];
	int				numIndices;

	mdlVertex_t		vertexList[MDL_MAX_VERTICES], vertex;
	int				numVertices;

	// Load the file
	inObject = lwGetObject(srcName, &failId, &failPos);
	if (!inObject){
		printf("Couldn't find %s\n", srcName);
		return;
	}

	if (inObject->nlayers != 1){
		printf("LWO has invalid number of layers (%i)\n", inObject->nlayers);
		return;
	}

	inLayer = &inObject->layer[0];

	outHeader = malloc(sizeof(cmHeader_t));

	// Setup default ST map
	defaultSTAxis[0] = 0;
	defaultSTAxis[1] = 1;

	st[0] = 0.0f;
	st[1] = 0.0f;

	for (i = 0; i < 3; i++){
		min = inLayer->bbox[i+0];
		max = inLayer->bbox[i+3];

		size = max - min;

		if (size > st[0]){
			defaultSTAxis[1] = defaultSTAxis[0];
			defaultSTAxis[0] = i;

			st[1] = st[0];
			st[0] = size;
		}
		else if (size > st[1]){
			defaultSTAxis[1] = i;

			st[1] = size;
		}
	}

	defaultXYZ2STScale[0] = 4.0f / st[0];
	defaultXYZ2STScale[1] = 4.0f / st[1];

	// Copy the meshes
	inSurface = inObject->surf;

	outHeader->numMeshes = inObject->nsurfs;
	clipMeshes = outMesh = malloc(inObject->nsurfs * sizeof(clipMesh_t));

	for (i = 0; i < outHeader->numMeshes; i++, outMesh++){
		// Copy the material
		strcpy(material, inSurface->name);
		if (strchr(material, '.'))
			*strchr(material, '.') = 0;

		memset(outMesh->mesh.material, 0, sizeof(outMesh->mesh.material));
		strcpy(outMesh->mesh.material, material);

		FlagsForShader(outMesh->mesh.material, &outMesh->mesh.surfaceFlags, &outMesh->mesh.contentFlags, &deform, &deformSize);

		// Copy vertices
		numIndices = 0;
		numVertices = 0;

		for (j = 0, inPolygon = inLayer->polygon.pol; j < inLayer->polygon.count; j++, inPolygon++){
			if (inPolygon->surf != inSurface)
				continue;

			if (inPolygon->type != ID_FACE){
				printf("WARNING: polygon type != ID_FACE, discarded...\n");
				continue;
			}

			if (inPolygon->nverts != 3){
				printf("WARNING: polygon has %i vertices, discarded...\n", inPolygon->nverts);
				continue;
			}

			for (k = 0, inPolVert = inPolygon->v; k < 3; k++, inPolVert++){
				inPoint = &inLayer->point.pt[inPolVert->index];

				vertex.xyz[0] = inPoint->pos[0];
				vertex.xyz[1] = inPoint->pos[2];
				vertex.xyz[2] = inPoint->pos[1];

				vertex.st[0] = vertex.xyz[defaultSTAxis[0]] * defaultXYZ2STScale[0];
				vertex.st[1] = vertex.xyz[defaultSTAxis[1]] * defaultXYZ2STScale[1];

				for (l = 0, inVMapPt = inPoint->vm; l < inPoint->nvmaps; l++, inVMapPt++){
					if (inVMapPt->vmap->type == LWID_('T', 'X', 'U', 'V')){
						vertex.st[0] = inVMapPt->vmap->val[inVMapPt->index][0];
						vertex.st[1] = 1.0f - inVMapPt->vmap->val[inVMapPt->index][1];
					}
				}

				for (l = 0, inVMapPt = inPolVert->vm; l < inPolVert->nvmaps; l++, inVMapPt++){
					if (inVMapPt->vmap->type == LWID_('T', 'X', 'U', 'V')){
						vertex.st[0] = inVMapPt->vmap->val[inVMapPt->index][0];
						vertex.st[1] = 1.0f - inVMapPt->vmap->val[inVMapPt->index][1];
					}
				}

				// Add it to the vertex list if not added yet
				if (numIndices * 3 == MDL_MAX_TRIANGLES){
					printf("MDL_MAX_TRIANGLES hit\n");
					return;
				}

				for (l = 0; l < numVertices; l++){
					if (vertexList[l].xyz[0] != vertex.xyz[0] || vertexList[l].xyz[1] != vertex.xyz[1] || vertexList[l].xyz[2] != vertex.xyz[2])
						continue;
					if (vertexList[i].st[0] != vertex.st[0] || vertexList[i].st[1] != vertex.st[1])
						continue;

					break;
				}

				if (l == numVertices){
					if (numVertices == MDL_MAX_VERTICES){
						printf("MDL_MAX_VERTICES hit\n");
						return;
					}

					indexList[numIndices++] = numVertices;
					vertexList[numVertices++] = vertex;
				}
				else
					indexList[numIndices++] = l;
			}
		}

		// Copy triangle indices
		for (j = 0; j < numIndices; j += 3){
			indices[j*3+0] = indexList[j+0];
			indices[j*3+1] = indexList[j+1];
			indices[j*3+2] = indexList[j+2];
		}

		// Copy vertices
		for (j = 0; j < numVertices; j++){
			points[j][0] = vertexList[j].xyz[0];
			points[j][1] = vertexList[j].xyz[1];
			points[j][2] = vertexList[j].xyz[2];
		}

		// Create the internal facet structure
		pc = CM_GenerateTriangleCollide(numVertices, points, numIndices / 3, indices, deform, deformSize);

		outMesh->mesh.numPlanes = pc->numPlanes;
		if (outMesh->mesh.numPlanes > CM_MAX_PLANES){
			printf("CM_MAX_PLANES");
			exit(1);
		}

		outMesh->planes = malloc(pc->numPlanes * sizeof(cmPlane_t));

		for (j = 0; j < pc->numPlanes; j++){
			outMesh->planes[j].normal[0] = pc->planes[j].plane[0];
			outMesh->planes[j].normal[1] = pc->planes[j].plane[1];
			outMesh->planes[j].normal[2] = pc->planes[j].plane[2];
			outMesh->planes[j].dist = pc->planes[j].plane[3];
		}

		outMesh->mesh.numFacets = pc->numFacets;
		if (outMesh->mesh.numFacets > CM_MAX_FACETS){
			printf("CM_MAX_FACETS");
			exit(1);
		}

		outMesh->facets = malloc(pc->numFacets * sizeof(cmFacet_t));

		for (j = 0; j < pc->numFacets; j++){
			if (pc->facets[j].numBorders > 21){
				printf("numBorders > 21");
				exit(1);
			}

			outMesh->facets[j].facetPlane = pc->facets[j].facetPlane;
			outMesh->facets[j].numBorders = pc->facets[j].numBorders;
			outMesh->facets[j].borderInward = 0;

			for (k = 0; k < 21; k++){
				outMesh->facets[j].borderPlanes[k] = pc->facets[j].borderPlanes[k];

				// To save space, we pack these into a single integer
				if (pc->facets[j].borderInward[k])
					outMesh->facets[j].borderInward |= (1<<k);
			}
		}

		VectorCopy(pc->bounds[0], outMesh->mesh.mins);
		VectorCopy(pc->bounds[1], outMesh->mesh.maxs);

		// Skip to next surface
		inSurface = inSurface->next;
	}

	lwFreeObject(inObject);

	// Compute bounds
	outHeader->mins[0] = outHeader->mins[1] = outHeader->mins[2] = 999999;
	outHeader->maxs[0] = outHeader->maxs[1] = outHeader->maxs[2] = -999999;

	for (i = 0, outMesh = clipMeshes; i < outHeader->numMeshes; i++, outMesh++){
		AddPointToBounds(outMesh->mesh.mins, outHeader->mins, outHeader->maxs);
		AddPointToBounds(outMesh->mesh.maxs, outHeader->mins, outHeader->maxs);

		// Also set offsets
		outMesh->mesh.ofsPlanes = sizeof(cmMesh_t);
		outMesh->mesh.ofsFacets = outMesh->mesh.ofsPlanes + (outMesh->mesh.numPlanes * sizeof(cmPlane_t));
		outMesh->mesh.ofsEnd = outMesh->mesh.ofsFacets + (outMesh->mesh.numFacets * sizeof(cmFacet_t));
	}

	outHeader->ident = CM_IDENT;
	outHeader->version = CM_VERSION;
	outHeader->ofsMeshes = sizeof(cmHeader_t);

	// Write the file
	f = fopen(dstName, "wb");
	if (!f){
		printf("Couldn't write %s\n", dstName);
		return;
	}

	fwrite(outHeader, 1, sizeof(cmHeader_t), f);

	for (i = 0, outMesh = clipMeshes; i < outHeader->numMeshes; i++, outMesh++){
		fwrite(&outMesh->mesh, 1, sizeof(cmMesh_t), f);
		fwrite(outMesh->planes, 1, outMesh->mesh.numPlanes * sizeof(cmPlane_t), f);
		fwrite(outMesh->facets, 1, outMesh->mesh.numFacets * sizeof(cmFacet_t), f);

		free(outMesh->planes);
		free(outMesh->facets);
	}

	free(clipMeshes);
	free(outHeader);

	fclose(f);

	printf("Wrote %s\n", dstName);
}

/*
 =================
 ConvertMD5MeshToMD5Clip
 =================
*/
static void ConvertMD5MeshToMD5Clip (const char *srcName, const char *dstName){

	FILE	*f;
	char	*data, *text, *ptr, *ptr2;
	int		len;
	char	shader[256];
	int		surfaceFlags, contentFlags, deform;
	float	deformSize;

	// Load the file
	f = fopen(srcName, "rb");
	if (!f){
		printf("Couldn't find %s\n", srcName);
		return;
	}

	fseek(f, 0, SEEK_END);
	len = ftell(f);
	fseek(f, 0, SEEK_SET);

	data = malloc(len + 1);
	data[len] = 0;
	fread(data, 1, len, f);
	fclose(f);

	// Write the file
	f = fopen(dstName, "wb");
	if (!f){
		printf("Couldn't write %s\n", dstName);
		return;
	}

	text = data;
	while (1){
		// Find a mesh
		ptr = strstr(text, "mesh {");
		if (!ptr)
			break;

		// Find the shader for this mesh
		ptr = strstr(ptr, "shader \"");
		if (!ptr){
			printf("Bad MD5 file\n");

			fclose(f);
			remove(dstName);
			return;
		}

		// Step over 'shader "'
		ptr += 8;

		// Copy shader name
		strncpy(shader, ptr, sizeof(shader));
		shader[sizeof(shader)-1] = 0;

		if (strchr(shader, '"'))
			*strchr(shader, '"') = 0;

		// Get shader flags
		FlagsForShader(shader, &surfaceFlags, &contentFlags, &deform, &deformSize);

		// Step over shader name
		ptr += strlen(shader) + 1;

		// Write text up to this point
		*ptr = 0;
		fprintf(f, "%s\n", text);
		text = ptr + 1;

		// Write the shader flags
		fprintf(f, "\n");
		fprintf(f, "\tsurfaceFlags %i\n", surfaceFlags);
		fprintf(f, "\tcontentFlags %i\n", contentFlags);
		fprintf(f, "\n");

		// Find 'numverts' (or 'numVerts')
		ptr = strstr(text, "numverts");
		if (!ptr){
			ptr = strstr(text, "numVerts");
			if (!ptr){
				printf("Bad MD5 file\n");

				fclose(f);
				remove(dstName);
				return;
			}
		}

		if (*(ptr-1) == '\t')
			ptr--;

		text = ptr;

		// Find 'numtris' (or 'numTris')
		ptr2 = strstr(text, "numtris");
		if (!ptr2){
			ptr2 = strstr(text, "numTris");
			if (!ptr2){
				printf("Bad MD5 file\n");

				fclose(f);
				remove(dstName);
				return;
			}
		}

		if (*(ptr2-1) == '\t')
			ptr2--;

		// Now we'll remove the ST coords since they aren't needed in the clip model
		while (1){
			// Find the opening parenthesis
			ptr = strchr(text, '(');
			if (ptr > ptr2)
				break;		// Past 'numtris'

			if (!ptr){
				printf("Bad MD5 file\n");

				fclose(f);
				remove(dstName);
				return;
			}

			// Write text up to this point
			*ptr = 0;
			fprintf(f, "%s", text);
			text = ptr + 1;

			// Find the closing parenthesis
			ptr = strstr(text, ") ");
			if (!ptr){
				printf("Bad MD5 file\n");

				fclose(f);
				remove(dstName);
				return;
			}

			text = ptr + 2;
		}
	}

	// Write any remaining text
	if (*text)
		fprintf(f, "%s", text);

	fclose(f);

	free(data);

	printf("Wrote %s\n", dstName);
}


// =====================================================================


int main (int argc, char **argv){

	char	src[1024], dst[1024];

	printf("ODModel v1.0 (C)2007 Team Blur.\n");

	if (argc != 3){
		printf("Usage:\n");
		printf("   odmodel -mdl <modelFile>\n");
		printf("   odmodel -cm <modelFile>\n");
		printf("   odmodel -md5 <modelFile>\n");
		return 0;
	}

	// This is very crappy
	if (!strcmp(argv[1], "-mdl")){
		strcpy(src, argv[2]);
		strlwr(src);
		if (!strchr(src, '.'))
			strcat(src, ".md3");

		strcpy(dst, src);
		*strchr(dst, '.') = 0;
		strcat(dst, ".mdl");

		if (strstr(src, ".md3"))
			ConvertMD3ToMDL(src, dst);
		else if (strstr(src, ".ase"))
			ConvertASEToMDL(src, dst);
		else if (strstr(src, ".lwo"))
			ConvertLWOToMDL(src, dst);
		else
			printf("Unsupported file extension for %s\n", src);
	}
	else if (!strcmp(argv[1], "-cm")){
		strcpy(src, argv[2]);
		strlwr(src);
		if (!strchr(src, '.'))
			strcat(src, ".md3");

		strcpy(dst, src);
		*strchr(dst, '.') = 0;
		strcat(dst, ".cm");

		if (strstr(src, ".md3"))
			ConvertMD3ToCM(src, dst);
		else if (strstr(src, ".ase"))
			ConvertASEToCM(src, dst);
		else if (strstr(src, ".lwo"))
			ConvertLWOToCM(src, dst);
		else
			printf("Unsupported file extension for %s\n", src);
	}
	else if (!strcmp(argv[1], "-md5")){
		strcpy(src, argv[2]);
		strlwr(src);
		if (!strchr(src, '.'))
			strcat(src, ".md5mesh");

		strcpy(dst, src);
		*strchr(dst, '.') = 0;
		strcat(dst, ".md5clip");

		ConvertMD5MeshToMD5Clip(src, dst);
	}
	else
		printf("Invalid parameters\n");

	return 0;
}
